當(dāng)Sklearn遇上Plotly,會(huì)擦出怎樣的火花?
導(dǎo)讀:在學(xué)習(xí)sklearn(機(jī)器學(xué)習(xí))過程中,模型原理可謂是枯燥無味,加上大多數(shù)模型訓(xùn)練過程也是不可見的,這使得很多小伙伴們望而卻步,當(dāng)然也有很多學(xué)者試圖通過各種方式以可視化模型學(xué)習(xí)及預(yù)測(cè)過程,但大多數(shù)是復(fù)雜且不美觀的。
本文將給大家?guī)硪粋€(gè)福音。當(dāng)機(jī)器學(xué)習(xí)遇到簡(jiǎn)潔、強(qiáng)大且美觀的plotly可視化庫時(shí),可謂是強(qiáng)強(qiáng)聯(lián)手,從模型訓(xùn)練、預(yù)測(cè)、決策邊界、殘差、交叉驗(yàn)證、網(wǎng)格搜索到模型評(píng)價(jià),均可以很容易地可視化出來。

Plotly基本介紹
官網(wǎng)鏈接:https://plot.ly/python/
Plotly 是一款用來做數(shù)據(jù)分析和可視化的在線平臺(tái),功能非常強(qiáng)大,可以在線繪制很多圖形比如條形圖、散點(diǎn)圖、餅圖、直方圖等等。而且還是支持在線編輯,以及多種語言python、javascript、matlab、R等許多API。
Plotly在Python中使用也很簡(jiǎn)單,直接用pip install plotly就可以了。推薦最好在Jupyter notebook中使用,Pycharm操作不是很方便。
Plotly的圖表多樣化且專業(yè)化,可以繪制很多專業(yè)學(xué)科領(lǐng)域的圖表。下面是官網(wǎng)的幾種劃分。
基本圖表

基礎(chǔ)圖表

統(tǒng)計(jì)圖

科學(xué)圖

金融圖表

地圖

3D圖表

多子圖

與Jupyter交互圖

添加自定義控件

人工智能與機(jī)器學(xué)習(xí)圖

本文主要深入探討poltly與機(jī)器學(xué)習(xí)結(jié)合,繪制機(jī)器學(xué)習(xí)相關(guān)圖。
注意:正文中繪圖代碼僅展示部分核心代碼,完整代碼可聯(lián)系原文作者云朵君獲取!
Plotly Express 回歸
這里我們將一起學(xué)習(xí)如何使用plotly圖表來顯示各種類型的回歸模型,從簡(jiǎn)單的模型如線性回歸,到其他機(jī)器學(xué)習(xí)模型如決策樹和多項(xiàng)式回歸。
重點(diǎn)學(xué)習(xí)plotly的各種功能,如使用不同參數(shù)對(duì)同一模型進(jìn)行比較分析、Latex顯示、3D表面圖,以及使用plotly Express進(jìn)行增強(qiáng)的預(yù)測(cè)誤差分析。
Plotly Express 簡(jiǎn)介
Plotly Express 是plotly的易于使用的高級(jí)界面,可處理多種類型的數(shù)據(jù)并生成易于樣式化的圖形。
通過Plotly Express 可以將普通最小二乘回歸趨勢(shì)線添加到帶有trendline參數(shù)的散點(diǎn)圖中。為此需要安裝statsmodels及其依賴項(xiàng)。
基礎(chǔ)圖形: scatter, line, area, bar, funnel, timeline
部分到整體圖表: pie, sunburst, treemap, funnel_area
一維分布圖: histogram, box, violin, strip
二維分布圖: density_heatmap, density_contour
矩陣的輸入圖: imshow
三維圖: scatter_3d, line_3d
多維圖: scatter_matrix, parallel_coordinates, parallel_categories
平鋪地圖: scatter_mapbox, line_mapbox, choropleth_mapbox, density_mapbox
離線地圖: scatter_geo, line_geo, choropleth
極坐標(biāo)圖: scatter_polar, line_polar, bar_polar
三元圖: scatter_ternary, line_ternary
普通最小二乘回歸可視化
將線性普通最小二乘(OLS)回歸趨勢(shì)線或非線性局部加權(quán)散點(diǎn)圖平滑(LOWESS)趨勢(shì)線添加到Python中的散點(diǎn)圖。將鼠標(biāo)懸停在趨勢(shì)線上將顯示該線的方程式及其R平方值,非常方便。
單線擬合
與seaborn類似,plotly圖表主題不需要單獨(dú)設(shè)置,使用默認(rèn)參數(shù)即可滿足正常情況下的使用,因此一行代碼并設(shè)置參數(shù)trendline="ols"即可搞定散點(diǎn)圖與擬合線的繪制,非常方便。
import plotly.express as px
fig=px.scatter(df, x="open", y="close",
trendline="ols")
fig.show()

多線擬合
同樣,在繪制多個(gè)變量及多個(gè)子圖時(shí),也不需要設(shè)置多畫布,只要設(shè)置好參數(shù) 'x','y','facet_col','color' 即可。
fig=px.scatter(df, x="open", y="close",
facet_col="Increase_Decrease",
color="Up_Down", trendline="ols")
fig.show()

查看擬合結(jié)果
繪圖后,需要查看具體的各項(xiàng)統(tǒng)計(jì)學(xué)數(shù)據(jù),可以通過get_trendline_results方法,具體代碼與結(jié)果如下。
results = px.get_trendline_results(fig)
results.query(
"Up_Down == 'Up' and Increase_Decrease == '1'"
).px_fit_results.iloc[0].summary()

非線性回歸可視化
非線性回歸擬合是通過設(shè)置參數(shù)trendline="lowess"來實(shí)現(xiàn),Lowess是指局部加權(quán)線性回歸,它是一種非參數(shù)回歸擬合的方式。
fig = px.scatter(df2, x="date", y="open",
color="Increase_Decrease",
trendline="lowess")
fig.show()

Sklearn與Plotly組合
Scikit-learn是一個(gè)流行的機(jī)器學(xué)習(xí)(ML)庫,它提供了各種工具,用于創(chuàng)建和訓(xùn)練機(jī)器學(xué)習(xí)算法、特征工程、數(shù)據(jù)清理以及評(píng)估和測(cè)試模型。
這里使用Scikit-learn來分割和預(yù)處理我們的數(shù)據(jù),并訓(xùn)練各種回歸模型。
線性回歸可視化
可以使用Scikit-learn的線性回歸執(zhí)行相同的預(yù)測(cè)。與直接用plotly.express擬合普通最小二乘回歸不同,這是通過散點(diǎn)圖和擬合線組合的方式繪制圖形,這會(huì)更加靈活,除了添加普通線性回歸擬合曲線,還可以組合其他線性回歸曲線,即將擬合結(jié)果很好地可視化出來。
import plotly.graph_objects as go
from sklearn.linear_model import LinearRegression
X = df.open.values.reshape(-1, 1)
# 回歸模型訓(xùn)練
model = LinearRegression()
model.fit(X, df.close)
# 生產(chǎn)預(yù)測(cè)點(diǎn)
x_range = np.linspace(X.min(), X.max(), 100)
y_range = model.predict(x_range.reshape(-1, 1))
# 圖形繪制
fig = px.scatter(df, x='open', y='close', opacity=0.65)
fig.add_traces(go.Scatter(x=x_range, y=y_range, name='Regression Fit'))
fig.show()

模型泛化能力可視化
利用plotly可視化查看模型泛化能力,即需要比較模型分別在訓(xùn)練集與測(cè)試集上的擬合狀況。這里使用Scatter繪圖,可以通過用不同的顏色著色訓(xùn)練和測(cè)試數(shù)據(jù)點(diǎn),將訓(xùn)練集與測(cè)試集數(shù)據(jù)及擬合線繪制在同一張畫布上,即可很容易地看到模型是否能很好地?cái)M合測(cè)試數(shù)據(jù)。

KNN回歸可視化
KNN回歸的原理是從訓(xùn)練樣本中找到與新點(diǎn)在距離上最近的預(yù)定數(shù)量的幾個(gè)點(diǎn),并從這些點(diǎn)中預(yù)測(cè)標(biāo)簽。
KNN回歸的一個(gè)簡(jiǎn)單的實(shí)現(xiàn)是計(jì)算最近鄰K的數(shù)值目標(biāo)的平均值。另一種方法是使用K近鄰的逆距離加權(quán)平均值。
from sklearn.neighbors import KNeighborsRegressor
# 數(shù)據(jù)準(zhǔn)備
X = df2.open.values.reshape(-1, 1)
x_range = np.linspace(X.min(), X.max(), 100)
# 模型訓(xùn)練,weights='distance'及weights='uniform'
knn_dist = KNeighborsRegressor(10, weights='distance')
knn_dist.fit(X, df2.Returns)
y_dist = knn_dist.predict(x_range.reshape(-1, 1))
# 繪制散點(diǎn)圖及擬合曲線
fig = px.scatter(df2, x='open', y='Returns', color='Up_Down', opacity=0.65)
fig.add_traces(go.Scatter(x=x_range, y=y_uni, name='Weights: Uniform'))
# 'Weights: Distance'
fig.show()

多項(xiàng)式回歸可視化
線性回歸是如何擬合直線的,而KNN可以呈現(xiàn)非線性的形狀。除此之外,還可以通過使用scikit-learn的多項(xiàng)式特征為特征的n次冪擬合一個(gè)斜率,將線性回歸擴(kuò)展到多項(xiàng)式回歸。
使用Plotly,只需在方程前后添加$符號(hào),就可以在圖例和標(biāo)題中使用$\LaTeX$顯示擬合方程,即你可以看到多項(xiàng)式回歸擬合的系數(shù)。
# 定義圖例中多項(xiàng)式方程函數(shù)
def format_coefs(coefs):
equation_list = [f"{coef}x^{i}" for i,
coef in enumerate(coefs)]
equation = "$" + " + ".join(equation_list) + "$"
replace_map = {"x^0": "", "x^1": "x", '+ -': '- '}
for old, new in replace_map.items():
equation = equation.replace(old, new)
return equation
# 繪制散點(diǎn)圖
fig = px.scatter(df, x='open', y='High_Low', opacity=0.65)
# 利用循環(huán)方式繪制多項(xiàng)式擬合曲線
fig.add_traces(go.Scatter(x=x_range.squeeze(), y=y_poly, name=equation))

3D圖繪制支持向量機(jī)決策邊界
二維平面中,當(dāng)類標(biāo)簽給出時(shí),可以使用散點(diǎn)圖考察兩個(gè)屬性將類分開的程度。即用一條直線或者更復(fù)雜的曲線,將兩個(gè)屬性定義的平面分成區(qū)域,每個(gè)區(qū)域包含一個(gè)類的大部分對(duì)象,則可能基于這對(duì)指定的屬性構(gòu)造精確的分類器,如用于二分類的邏輯回歸。
而在更高維度中,即當(dāng)輸入數(shù)據(jù)中有多個(gè)變量時(shí),分類器可以是支持向量機(jī)(SVM),其通過在高維空間中尋找決策邊界以區(qū)分不同類別標(biāo)簽。如在三維空間中可以通3D圖內(nèi)的曲線來可視化模型的決策平面。
在Plotly中可以利用px.scatter_3d 和go.Surface繪制3D圖。
from sklearn.svm import SVR
# 建立模型
model = SVR(C=1.)
model.fit(X, y)
# 使用模型預(yù)測(cè)
pred = model.predict(np.c_[xx.ravel(), yy.ravel()])
pred = pred.reshape(xx.shape)
# 繪圖
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width')
fig.update_traces(marker=dict(size=5))
fig.add_traces(go.Surface(x=xrange, y=yrange,
z=pred, name='pred_surface'))

多元線性回歸可視化
本節(jié)介紹用plotly可視化多元線性回歸(MLR)的系數(shù)。
用一個(gè)或兩個(gè)變量可視化回歸是很簡(jiǎn)單的,因?yàn)榭梢苑謩e用散點(diǎn)圖和3D散點(diǎn)圖來繪制它們。但如果有兩個(gè)以上的特性,則需要找到其他方法來可視化數(shù)據(jù)。
一種方法是使用條形圖。下面列子中每個(gè)條形圖表示每個(gè)輸入特征的線性回歸模型的系數(shù)。柱狀圖等大小代表線性回歸系數(shù)的大小,負(fù)相關(guān)與正相關(guān)分別用紅色與藍(lán)色區(qū)分,特別顯目。
X = df.loc[:,['open', 'close','volume', 'Increase_Decrease']]
X = pd.get_dummies(X, columns=['Increase_Decrease'], prefix_sep='=')
y = df['Returns']
# 模型訓(xùn)練
model = LinearRegression()
model.fit(X, y)
# 繪制柱狀圖
fig = px.bar(
x=X.columns, y=model.coef_, color=colors,
color_discrete_sequence=['red', 'blue'],
labels=dict(x='Feature', y='Linear coefficient'),
title='Weight of each feature for predicting Returns'
)
fig.show()

實(shí)際點(diǎn)與預(yù)測(cè)點(diǎn)的比較圖
這介紹了比較預(yù)測(cè)輸出與實(shí)際輸出的最簡(jiǎn)單方法,即以真實(shí)值為x軸,以預(yù)測(cè)值為y值,繪制二維散點(diǎn)圖。從圖中看,若理論最優(yōu)擬合(黑色斜線)附近有大部分的散點(diǎn)則說明模型擬合效果很好。
y_pred = model.predict(X)
# 繪制散點(diǎn)圖
fig = px.scatter(x=y, y=y_pred,
labels={'x': 'ground truth',
'y': 'prediction'})
# 繪制理論最優(yōu)擬合
fig.add_shape(
type="line", line=dict(dash='dash'),
x0=y.min(), y0=y.min(),
x1=y.max(), y1=y.max())
fig.show()

增強(qiáng)的預(yù)測(cè)誤差分析圖
通過添加邊緣直方圖來快速診斷模型可能存在的任何預(yù)測(cè)誤差。通過將模型與理論最優(yōu)擬合(黑色虛線)進(jìn)行比較,內(nèi)置的OLS功能可以可視化模型的泛化程度。
邊緣的直方圖表示在某個(gè)區(qū)間內(nèi),模型與理論最優(yōu)擬合之間的誤差值,不同的顏色代表不同的數(shù)據(jù)集。
model = LinearRegression()
model.fit(X_train, y_train)
df['prediction'] = model.predict(X)
# 散點(diǎn)圖與擬合虛線
fig = px.scatter(
df, x='open', y='prediction',
marginal_x='histogram', marginal_y='histogram',
color='split', trendline='ols')
# 邊緣直方圖
fig.update_traces(histnorm='probability', selector={'type':'histogram'})
# 理論最優(yōu)擬合 黑色虛線
fig.add_shape(
type="line", line=dict(dash='dash'),
x0=y.min(), y0=y.min(),
x1=y.max(), y1=y.max())

殘差圖
就像預(yù)測(cè)誤差圖一樣,使用plotly很容易在幾行代碼中可視化預(yù)測(cè)殘差。即在常規(guī)的散點(diǎn)圖中設(shè)置預(yù)測(cè)參數(shù)trendline='ols'及預(yù)測(cè)殘差參數(shù)marginal_y='violin',并以小提琴的圖形展示出來。
# 模型訓(xùn)練
model = LinearRegression()
model.fit(X_train, y_train)
# 模型預(yù)測(cè)
df['prediction'] = model.predict(X)
df['residual'] = df['prediction'] - df['close']
# 繪制散點(diǎn)圖和擬合線
fig = px.scatter(
df, x='prediction', y='residual',
marginal_y='violin', # 設(shè)置殘差小提琴圖
color='split', trendline='ols')
fig.show()

交叉驗(yàn)證可視化
交叉驗(yàn)證是將訓(xùn)練數(shù)據(jù)再次分配,我們以5折為例,就是說將交叉數(shù)據(jù)分成五份,每次都選取不同的數(shù)據(jù)作為驗(yàn)證數(shù)據(jù)。每一組不同的驗(yàn)證數(shù)據(jù)都會(huì)得出一個(gè)準(zhǔn)確度,求得五組準(zhǔn)確度的平均值,就是某個(gè)參數(shù)情況下的準(zhǔn)確度。
Plotly可以使用Scikit-learn的LassoCV繪制交叉驗(yàn)證結(jié)果中各種懲罰值的結(jié)果。
from sklearn.linear_model import LassoCV
N_FOLD = 6
# 數(shù)據(jù)準(zhǔn)備
X = df.loc[:,['open', 'close', 'Open_Close',
'High_Low', 'volume',
'Increase_Decrease']]
X = pd.get_dummies(X, columns=['Increase_Decrease'],
prefix_sep='=')
y = df['Returns']
# 模型訓(xùn)練
model = LassoCV(cv=N_FOLD, normalize=True)
model.fit(X, y)
mean_alphas = model.mse_path_.mean(axis=-1)
# 繪制交叉驗(yàn)證均方誤差曲線
fig = go.Figure([
go.Scatter(
x=model.alphas_, y=model.mse_path_[:, i],
name=f"Fold: {i+1}", opacity=.5,
line=dict(dash='dash'),
hovertemplate="alpha: %{x} <br>MSE: %{y}")
for i in range(N_FOLD)])
# 添加交叉驗(yàn)證的平均均方誤差
fig.add_traces(go.Scatter(
x=model.alphas_, y=mean_alphas,
name='Mean', line=dict(color='black', width=3),
hovertemplate="alpha: %{x} <br>MSE: %{y}",))
fig.show()

基于決策樹的網(wǎng)格搜索可視化
Scikit-learn機(jī)器學(xué)習(xí)中的GridSearchCV,即GridSearch和CV,網(wǎng)格搜索和交叉驗(yàn)證。
網(wǎng)格搜索,搜索的是參數(shù),即在指定的參數(shù)范圍內(nèi),按步長依次調(diào)整參數(shù),利用調(diào)整的參數(shù)訓(xùn)練學(xué)習(xí)器,從所有的參數(shù)中找到在驗(yàn)證集上精度最高的參數(shù),這其實(shí)是一個(gè)訓(xùn)練和比較的過程。
Plotly中運(yùn)用px.density_heatmap 和 px.box,在DecisionTreeRegressor上將網(wǎng)格搜索過程可視化。
網(wǎng)格搜索調(diào)參
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeRegressor
N_FOLD = 6
X = df[['open', 'volume']]
y = df['close']
# 定義與訓(xùn)練網(wǎng)格搜索
model = DecisionTreeRegressor()
param_grid = {
'criterion': ['mse', 'friedman_mse', 'mae'],
'max_depth': range(2, 5)}
grid = GridSearchCV(model, param_grid, cv=N_FOLD)
grid.fit(X, y)
grid_df = pd.DataFrame(grid.cv_results_)
# 將網(wǎng)格的寬格式轉(zhuǎn)換為長格式

單個(gè)函數(shù)調(diào)用來繪制每個(gè)圖形
第一個(gè)圖顯示了如何在單個(gè)分割(使用facet分組)上可視化每個(gè)模型參數(shù)的分?jǐn)?shù)。
每個(gè)大塊代表不同數(shù)據(jù)分割下,不同網(wǎng)格參數(shù)的R方和。而其中每個(gè)小塊代表相同數(shù)據(jù)分割下,網(wǎng)格參數(shù):'criterion'與'max_depth'在不同取值組合下的R方和。
fig_hmap = px.density_heatmap(
melted, x="max_depth", y='criterion',
histfunc="sum", z="r_squared",
title='Grid search results on individual fold',
hover_data=['mean_fit_time'],
facet_col="cv_split", facet_col_wrap=3,
labels={'mean_test_score': "mean_r_squared"})
fig_hmap.show()

第二個(gè)圖匯總了所有分割的結(jié)果,每個(gè)盒子代表一個(gè)單一的模型。三組盒子代表三個(gè)不同的樹深度'max_depth',每組中不同顏色的盒子代表不同的評(píng)價(jià)標(biāo)準(zhǔn)'criterion'。
fig_box = px.box(
melted, x='max_depth', y='r_squared',
title='Grid search results ',
hover_data=['mean_fit_time'],
points='all',
color="criterion",
hover_name='cv_split',
labels={'mean_test_score': "mean_r_squared"})
fig_box.show()

KNN分類可視化
訓(xùn)練一個(gè) K-Nearest Neighbors 分類器,首先模型記錄每個(gè)訓(xùn)練樣本的標(biāo)簽。然后每當(dāng)給它一個(gè)新樣本時(shí),它就會(huì)從訓(xùn)練集中找k個(gè)最接近的樣本來找到對(duì)應(yīng)的標(biāo)簽,然后做投票,看看這個(gè)區(qū)域內(nèi),哪個(gè)類別標(biāo)簽數(shù)量多,以確定標(biāo)簽值并把它賦給新樣本。
在圖中,將所有負(fù)標(biāo)簽顯示為正方形,正標(biāo)簽顯示為圓形。我們通過在測(cè)試數(shù)據(jù)中心添加一個(gè)點(diǎn)來區(qū)分訓(xùn)練集和測(cè)試集。

通過plotly中的dash還可以繪制交互圖,不同參數(shù)下不同的決策邊界,無疑給我們理解模型提供了一個(gè)很好的幫手。具體繪圖過程可以到官網(wǎng)查看,這里不做過多的介紹。

模型評(píng)價(jià)可視化
這里的模型評(píng)價(jià)主要針對(duì)分類模型,回歸模型用擬合誤差、擬合殘差等可以評(píng)價(jià)回歸模型的優(yōu)劣,前面已經(jīng)介紹過了。此處主要是將模型的預(yù)測(cè)概率、模型效果可視化,如假正率真正率曲線圖、繪制ROC曲線圖等。
與真實(shí)標(biāo)簽相比的分?jǐn)?shù)直方圖
from sklearn.metrics import roc_curve, auc
# 二分類邏輯回歸建模
model = LogisticRegression()
model.fit(X, y)
# 模型預(yù)測(cè)概率
y_score = model.predict_proba(X)[:, 1]
# 繪制預(yù)測(cè)概率直方圖
fig_hist = px.histogram(
x=y_score, color=y, nbins=30,
labels=dict(color='True Labels', x='Score'))
fig_hist.show()

在不同的閾值下評(píng)估模型性能
# 計(jì)算ROC曲線各個(gè)值
fpr, tpr, thresholds = roc_curve(y, y_score)
# 建立閾值數(shù)據(jù)框
df = pd.DataFrame({
'False Positive Rate': fpr,
'True Positive Rate': tpr}
, index=thresholds)
df.index.name = "Thresholds"
df.columns.name = "Rate"
# 繪制折線圖
fig_thresh = px.line(
df, title='TPR and FPR at every threshold',
width=500, height=500)
# 設(shè)置x/y軸
fig_thresh.update_yaxes(scaleanchor="x", scaleratio=1)
fig_thresh.update_xaxes(range=[0.2, 1], constrain='domain')
fig_thresh.show()

# 繪制面積圖
fig = px.area(
x=fpr, y=tpr,
title=f'ROC Curve (AUC={auc(fpr, tpr):.4f})',
labels=dict(x='False Positive Rate',
y='True Positive Rate'),
width=700, height=500)
# 添加理論線 黑色虛線
fig.add_shape(
type='line', line=dict(dash='dash'),
x0=0, x1=1, y0=0, y1=1)
# 更新圖表樣式
fig.update_yaxes(scaleanchor="x", scaleratio=1)
fig.update_xaxes(constrain='domain')
fig.show()

推薦閱讀
