用 Python 繪制圖表理解神經(jīng)網(wǎng)絡
Python中文社區(qū)(ID:python-china)
人工神經(jīng)網(wǎng)絡 (ANN) 已成功應用于許多需要人工監(jiān)督的日常任務,但由于其復雜性,很難理解它們的工作方式和訓練方式。
在這篇博客中,我們深入討論了神經(jīng)網(wǎng)絡是什么、它們是如何工作的,以及如何將它們應用于諸如尋找異常值或預測金融時間序列之類的問題。
在這篇文章中,我嘗試直觀地展示一個簡單的前饋神經(jīng)網(wǎng)絡如何在訓練過程中將一組輸入映射到不同的空間,以便更容易理解它們。
數(shù)據(jù)
為了展示它是如何工作的,首先我創(chuàng)建了一個“ toy”數(shù)據(jù)集。它包含 400 個均勻分布在兩個類(0 和 1)中的樣本,每個樣本具有兩個維度(X0 和 X1)。
注:所有數(shù)據(jù)均來自三個隨機正態(tài)分布,均值為 [-1, 0, 1],標準差為 [0.5, 0.5, 0.5]。
網(wǎng)絡架構(gòu)
下一步是定義ANN的結(jié)構(gòu),如下:
隱藏層的維度最?。? 個神經(jīng)元)以顯示網(wǎng)絡在 2D 散點圖中映射每個樣本的位置。
盡管前面的圖表沒有顯示,但每一層都有一個修改其輸出的激活函數(shù)。
•輸入層有一個linear激活函數(shù)來復制它的輸入值。
•隱藏層具有ReLU或tanh激活函數(shù)。
•輸出層有一個sigmoid激活函數(shù),可以將其輸入值“縮小”到 [0, 1] 范圍內(nèi)。
訓練
除了網(wǎng)絡的架構(gòu)之外,神經(jīng)網(wǎng)絡的另一個關(guān)鍵方面是訓練過程。訓練 ANN 的方法有很多種,但最常見的是反向傳播過程。
反向傳播過程首先將所有訓練案例(或一批)前饋到網(wǎng)絡,然后優(yōu)化器根據(jù)損失函數(shù)計算“如何”更新網(wǎng)絡的權(quán)重,并根據(jù)學習率更新它們。
當損失收斂、經(jīng)過一定數(shù)量的 epoch 或用戶停止訓練時,訓練過程停止。一個epoch 表示所有的數(shù)據(jù)送入網(wǎng)絡中, 完成了一次前向計算 + 反向傳播的過程。
在我們的研究案例中,該架構(gòu)使用隱藏層中的 2 個不同激活函數(shù)(ReLU 和 Tanh)和 3 個不同的學習率(0.1、0.01 和 0.001)進行訓練。
在輸入樣本周圍,有一個“網(wǎng)格”點,顯示模型為該位置的樣本提供的預測概率。這使得模型在訓練過程中生成的邊界更加清晰。
- # figure holding the evolution
- f, axes = plt.subplots(1, 3, figsize=(18, 6), gridspec_kw={'height_ratios':[.9]})
- f.subplots_adjust(top=0.82)
- # camera to record the evolution
- camera = Camera(f)
- # number of epochs
- epochs = 20
- # iterate epoch times
- for i in range(epochs):
- # evaluate the model (acc, loss)
- evaluation = model.evaluate(x_train, y_train, verbose=0)
- # generate intermediate models
- model_hid_1 = Model(model.input, model.get_layer("hidden_1").output)
- model_act_1 = Model(model.input, model.get_layer("activation_1").output)
- # generate data
- df_hid_1 = pd.DataFrame(model_hid_1.predict(x_train), columns=['X0', 'X1'])
- df_hid_1['y'] = y_train
- df_act_1 = pd.DataFrame(model_act_1.predict(x_train), columns=['X0', 'X1'])
- df_act_1['y'] = y_train
- # generate meshgrid (200 values)
- x = np.linspace(x_train[:,0].min(), x_train[:,0].max(), 200)
- y = np.linspace(x_train[:,1].min(), x_train[:,1].max(), 200)
- xv, yv = np.meshgrid(x, y)
- # generate meshgrid intenisty
- df_mg_train = pd.DataFrame(np.stack((xv.flatten(), yv.flatten()), axis=1), columns=['X0', 'X1'])
- df_mg_train['y'] = model.predict(df_mg_train.values)
- df_mg_hid_1 = pd.DataFrame(model_hid_1.predict(df_mg_train.values[:,:-1]), columns=['X0', 'X1'])
- df_mg_hid_1['y'] = model.predict(df_mg_train.values[:,:-1])
- df_mg_act_1 = pd.DataFrame(model_act_1.predict(df_mg_train.values[:,:-1]), columns=['X0', 'X1'])
- df_mg_act_1['y'] = model.predict(df_mg_train.values[:,:-1])
- # show dataset
- ax = sns.scatterplot(x='X0', y='X1', data=df_mg_train, hue='y', x_jitter=True, y_jitter=True, legend=None, ax=axes[0], palette=sns.diverging_palette(220, 20, as_cmap=True), alpha=0.15)
- ax = sns.scatterplot(x='X0', y='X1', data=df_train, hue='y', legend=None, ax=axes[0], palette=sns.diverging_palette(220, 20, n=2))
- ax.set_title('Input layer')
- ax = sns.scatterplot(x='X0', y='X1', data=df_mg_hid_1, hue='y', x_jitter=True, y_jitter=True, legend=None, ax=axes[1], palette=sns.diverging_palette(220, 20, as_cmap=True), alpha=0.15)
- ax = sns.scatterplot(x='X0', y='X1', data=df_hid_1, hue='y', legend=None, ax=axes[1], palette=sns.diverging_palette(220, 20, n=2))
- ax.set_title('Hidden layer')
- # show the current epoch and the metrics
- ax.text(x=0.5, y=1.15, s='Epoch {}'.format(i+1), fontsize=16, weight='bold', ha='center', va='bottom', transform=ax.transAxes)
- ax.text(x=0.5, y=1.08, s='Accuracy {:.3f} - Loss {:.3f}'.format(evaluation[1], evaluation[0]), fontsize=13, ha='center', va='bottom', transform=ax.transAxes)
- ax = sns.scatterplot(x='X0', y='X1', data=df_mg_act_1, hue='y', x_jitter=True, y_jitter=True, legend=None, ax=axes[2], palette=sns.diverging_palette(220, 20, as_cmap=True), alpha=0.15)
- ax = sns.scatterplot(x='X0', y='X1', data=df_act_1, hue='y', legend=None, ax=axes[2], palette=sns.diverging_palette(220, 20, n=2))
- ax.set_title('Activation')
- # show the plot
- plt.show()
- # call to generate the GIF
- camera.snap()
- # stop execution if loss <= 0.263 (avoid looping 200 times if not needed)
- if evaluation[0] <= 0.263:
- break
- # train the model 1 epoch
- model.fit(x_train, y_train, epochs=1, verbose=0)
ReLU 激活
Tanh 激活
注意:使用的損失函數(shù)是二元交叉熵,因為我們正在處理二元分類問題,而優(yōu)化器是對原始隨機梯度下降 (SGD) 稱為 Adam 的修改。當epoch達到 200 或損失低于 0.263 時,模型訓練停止。