代碼詳解:使用NumPy,教你9步從頭搭建神經網絡

2021-01-11 讀芯術

全文共2875字,預計學習時長20分鐘或更長

Photo by Alina Grubnyak on Unsplash

如果你是個對神經網絡有所了解的初級數據科學家,或是個對深度學習略有耳聞的機器學習愛好者,一定要讀一讀這篇文章。本文介紹了使用NumPy從頭搭建神經網絡的9個步驟,即從數據預處理到反向傳播這一「必經之路」。

對機器學習、人工神經網絡、Python語法和編程邏輯有些基本理解最好,(但這也不是必需條件,你可以邊讀邊學)。

1. 初始化

導入NumPy。

import numpy as np

np.random.seed(42) # for reproducibility

2. 生成數據

深度學習需要大量的數據。網上有很多乾淨的數據集,但為了使用簡單,會選擇生成自己的數據集,即輸入a和b,輸出a + b,a-b和| a-b |。這樣可以生成10,000個基準點。

X_num_row, X_num_col = [2, 10000] # Row is no. of feature, col is no.of datum points

X_raw = np.random.rand(X_num_row,X_num_col) * 100

y_raw = np.concatenate(([(X_raw[0,:] + X_raw[1,:])], [(X_raw[0,:] -X_raw[1,:])], np.abs([(X_raw[0,:] - X_raw[1,:])])))

# for input a and b, output is a+b; a-b and |a-b|

y_num_row, y_num_col = y_raw.shape

Photo by Kristopher Roller on Unspla

3. 分割測試集與訓練集

將數據集劃分為訓練集(佔70%)和測試集(30%)兩個子集。訓練集只用於神經網絡的調整,測試集則是在訓練完成後用來測試性能。

train_ratio = 0.7

num_train_datum = int(train_ratio*X_num_col)

X_raw_train = X_raw[:,0:num_train_datum]

X_raw_test = X_raw[:,num_train_datum:]

y_raw_train = y_raw[:,0:num_train_datum]

y_raw_test = y_raw[:,num_train_datum:]

4. 數據標準化

訓練集中的數據已進行了標準化處理,所以各標準化特徵都呈零均值、單位方差的分布。然後可以將上述過程產生的定標器應用於測試集。

class scaler:

def __init__(self, mean,std):

self.mean = mean

self.std = std

def get_scaler(row):

mean = np.mean(row)

std = np.std(row)

return scaler(mean, std)

def standardize(data, scaler):

return (data -scaler.mean) / scaler.std

def unstandardize(data, scaler):

return (data * scaler.std) +scaler.mean

# Construct scalers from training set

X_scalers = [get_scaler(X_raw_train[row,:]) for row inrange(X_num_row)]

X_train = np.array([standardize(X_raw_train[row,:], X_scalers[row]) forrow in range(X_num_row)])

y_scalers = [get_scaler(y_raw_train[row,:]) for row inrange(y_num_row)]

y_train = np.array([standardize(y_raw_train[row,:], y_scalers[row]) forrow in range(y_num_row)])

# Apply those scalers to testing set

X_test = np.array([standardize(X_raw_test[row,:], X_scalers[row]) forrow in range(X_num_row)])

y_test = np.array([standardize(y_raw_test[row,:], y_scalers[row]) forrow in range(y_num_row)])

# Check if data has been standardized

print([X_train[row,:].mean() for row in range(X_num_row)]) # should beclose to zero

print([X_train[row,:].std() for row in range(X_num_row)]) # should be close to one

print([y_train[row,:].mean() for row in range(y_num_row)]) # should beclose to zero

print([y_train[row,:].std() for row in range(y_num_row)]) # should be close to one

因此,定標器不含有測試集的任何信息。這也是在對神經網絡進行調整前我們所希望的結果。

至此,歷經上述4個步驟後,數據預處理就完成了。

5. 搭建神經網絡

Photo by freestocks.org on Unsplash

使用Python中的類對『層』進行對象化。每個層(輸入層除外)具有權重矩陣W、偏置矢量b和激活函數。各個層都將被附加到名為neural_net的列表中,從而得到全連接的神經網絡。

class layer:

def __init__(self,layer_index, is_output, input_dim, output_dim, activation):

self.layer_index = layer_index# zero indicates input layer

self.is_output =is_output # true indicates output layer, false otherwise

self.input_dim =input_dim

self.output_dim =output_dim

self.activation =activation

# the multiplicationconstant is sorta arbitrary

if layer_index != 0:

self.W = np.random.randn(output_dim,input_dim) * np.sqrt(2/input_dim)

self.b =np.random.randn(output_dim, 1) * np.sqrt(2/input_dim)

# Change layers_dim to configure your own neural net!

layers_dim = [X_num_row, 4, 4, y_num_row] # input layer --- hiddenlayers --- output layers

neural_net = []

# Construct the net layer by layer

for layer_index in range(len(layers_dim)):

if layer_index == 0: # ifinput layer

neural_net.append(layer(layer_index, False, 0, layers_dim[layer_index],'irrelevant'))

elif layer_index+1 ==len(layers_dim): # if output layer

neural_net.append(layer(layer_index, True, layers_dim[layer_index-1],layers_dim[layer_index], activation='linear'))

else:

neural_net.append(layer(layer_index,False, layers_dim[layer_index-1], layers_dim[layer_index], activation='relu'))

# Simple check on overfitting

pred_n_param =sum([(layers_dim[layer_index]+1)*layers_dim[layer_index+1] for layer_index inrange(len(layers_dim)-1)])

act_n_param = sum([neural_net[layer_index].W.size +neural_net[layer_index].b.size for layer_index in range(1,len(layers_dim))])

print(f'Predicted number of hyperparameters: {pred_n_param}')

print(f'Actual number of hyperparameters: {act_n_param}')

print(f'Number of data: {X_num_col}')

if act_n_param >= X_num_col:

raise Exception('It willoverfit.')

最後,使用下列公式,通過計數對超參數的數量進行完整性檢查。可用的基準數量應該多於超參數數量,否則就會過度擬合。

N ^ l是第l層的超參數個數,L是層數(不包括輸入層)。

6. 前向傳播

在給定一組權重和偏差的情況下,定義一個前向傳播的函數。各層之間的連接以矩陣形式定義為:

σ 是element-wise 激活函數,上標T表示矩陣的轉置。

def activation(input_, act_func):

if act_func == 'relu':

return np.maximum(input_,np.zeros(input_.shape))

elif act_func == 'linear':

return input_

else:

raiseException('Activation function is not defined.')

def forward_prop(input_vec, layers_dim=layers_dim,neural_net=neural_net):

neural_net[0].A = input_vec #Define A in input layer for for-loop convenience

for layer_index inrange(1,len(layers_dim)): # W,b,Z,A are undefined in input layer

neural_net[layer_index].Z= np.add(np.dot(neural_net[layer_index].W, neural_net[layer_index-1].A),neural_net[layer_index].b)

neural_net[layer_index].A =activation(neural_net[layer_index].Z, neural_net[layer_index].activation)

return neural_net[layer_index].A

激活函數是逐個定義的。 ReLU實現為a→max(a,0),而sigmoid函數應返回a → 1/(1+e^(-a)),其實現就留給讀者作為練習吧。

Photo by Holger Link on Unsplash

7. 反向傳播

這是最棘手的一步,很多人都不明白。在定義了用於評估性能的損失度量函數後,就想看一看如果擾亂每個權重或偏差,損失度量會如何變化。即每個權重和偏差對損失度量的敏感程度如何。

def get_loss(y, y_hat, metric='mse'):

if metric == 'mse':

individual_loss = 0.5 *(y_hat - y) ** 2

returnnp.mean([np.linalg.norm(individual_loss[:,col], 2) for col inrange(individual_loss.shape[1])])

else:

raise Exception('Loss metric is notdefined.')

def get_dZ_from_loss(y, y_hat, metric):

if metric == 'mse':

return y_hat - y

else:

raise Exception('Lossmetric is not defined.')

def get_dactivation(A, act_func):

if act_func == 'relu':

returnnp.maximum(np.sign(A), np.zeros(A.shape)) # 1 if backward input >0, 0 otherwise;then diaganolize

elif act_func == 'linear':

return np.ones(A.shape)

else:

raiseException('Activation function is not defined.')

def backward_prop(y, y_hat, metric='mse', layers_dim=layers_dim,neural_net=neural_net, num_train_datum=num_train_datum):

for layer_index inrange(len(layers_dim)-1,0,-1):

if layer_index+1 ==len(layers_dim): # if output layer

dZ =get_dZ_from_loss(y, y_hat, metric)

else:

dZ = np.multiply(np.dot(neural_net[layer_index+1].W.T,dZ),

get_dactivation(neural_net[layer_index].A,neural_net[layer_index].activation))

dW = np.dot(dZ,neural_net[layer_index-1].A.T) / num_train_datum

db = np.sum(dZ, axis=1,keepdims=True) / num_train_datum

neural_net[layer_index].dW = dW

neural_net[layer_index].db = db

這通過偏導數e/W(在代碼中表示為dW)和e/b(在代碼中表示為db)來表示,還可以通過分析計算。

這些反向傳播方程都假設只有y這一個比較數據。每次迭代的性能僅受一個基準點的影響,所以梯度更新過程會非常嘈雜。為減少噪聲,可以使用多個基準。其中W(y_1,y_2,...)是W(y_1),W(y_2),...的平均值,b也一樣。這些在方程中都沒有顯示出來,但在下面的代碼中可以實現。

8. 迭代優化

現在我們有了訓練神經網絡的全部要素。

知道權重和偏差的敏感性後,可使用以下更新規則通過梯度下降來迭代最小化(因此用減號表示)損失度量:

W = W - learning_rate * W

b = b - learning_rate * b

Photo by Rostyslav Savchyn on Unspla

learning_rate = 0.01

max_epoch = 100000

for epoch in range(1,max_epoch+1):

y_hat_train =forward_prop(X_train) # update y_hat

backward_prop(y_train,y_hat_train) # update (dW,db)

for layer_index inrange(1,len(layers_dim)): # update(W,b)

neural_net[layer_index].W= neural_net[layer_index].W - learning_rate * neural_net[layer_index].dW

neural_net[layer_index].b= neural_net[layer_index].b - learning_rate * neural_net[layer_index].db

if epoch % 100000 == 0:

print(f'{get_loss(y_train, y_hat_train):.4f}')

9. 測試

如果測試損失沒有超出訓練損失太多,該模型就可以進行推廣。為了解模型的執行情況,我們還製作了一些測試用例。

print(get_loss(y_test, forward_prop(X_test)))

def predict(X_raw_any):

X_any =np.array([standardize(X_raw_any[row,:], X_scalers[row]) for row inrange(X_num_row)])

y_hat = forward_prop(X_any)

y_hat_any =np.array([unstandardize(y_hat[row,:], y_scalers[row]) for row in range(y_num_row)])

return y_hat_any

predict(np.array([[30,70],[70,30],[3,5],[888,122]]).T)

這就是使用NumPy從頭搭建神經網絡的9個步驟。本文所講述的並非構建和訓練神經網絡最有效的方法,在未來還有很大的改進空間。有人可能用過一些高級框架(如TensorFlow、PyTorch或Keras)搭建神經網絡。不過,僅用低級庫進行搭建可以讓我們真正弄明白這一神秘科學背後的原理。

留言 點讚 關注

我們一起分享AI學習與發展的乾貨

歡迎關注全平臺AI垂類自媒體 「讀芯術」

相關焦點

  • 一篇文章教你用11行Python代碼實現神經網絡
    聲明:本文是根據英文教程 (用 11 行 Python 代碼實現的神經網絡)學習總結而來,關於更詳細的神經網絡的介紹可以參考我的另一篇博客:。A Neural Network in 11 lines of Python從感知機到人工神經網絡如果你讀懂了下面的文章,你會對神經網絡有更深刻的認識,有任何問題,請多指教。
  • TensorFlow什麼的都弱爆了,強者只用Numpy搭建神經網絡
    大數據文摘出品作者:蔣寶尚很多同學入門機器學習之後,直接用TensorFlow調包實現神經網絡,對於神經網絡內在機理知之甚少。程式語言與技術框架變化更新非常之快,理解背後的原理才是王道。下面文摘菌和大家一起用Numpy實現一步一步實現神經網絡。
  • 如何用Paddle Fluid API搭建簡單的神經網絡?這裡有一份編程指南
    本文將展示如何用 Paddle Fluid API 編程並搭建一個簡單的神經網絡。使用 Operator 表示對數據的操作在 Paddle Fluid 中,所有對數據的操作都由 Operator 表示,您可以使用內置指令來描述它們的神經網絡。為了便於用戶使用,在 Python 端,Paddle Fluid 中的 Operator 被一步封裝入 paddle.fluid.layers,paddle.fluid.nets 等模塊。
  • 代碼詳解:基於Python建立任意層數的深度神經網絡 - 讀芯術
    圖1 神經網絡構造的例子(符號說明:上標[l]表示與第l層;上標(i)表示第i個例子;下標i表示矢量第i項)單層神經網絡圖2 單層神經網絡示例神經元模型是先計算一個線性函數(z=Wx+b),接著再計算一個激活函數。一般來說,神經元模型的輸出值是a=g(Wx+b),其中g是激活函數(sigmoid,tanh, ReLU, …)。
  • 深度學習筆記8:利用Tensorflow搭建神經網絡
    數據挖掘與機器學習,R與Python,理論與實踐並行。個人公個人公眾號:數據科學家養成記 (微信ID:louwill12)前文傳送門:深度學習筆記1:利用numpy從零搭建一個神經網絡深度學習筆記2:手寫一個單隱層的神經網絡深度學習筆記3:手動搭建深度神經網絡(DNN)深度學習筆記4:深度神經網絡的正則化深度學習筆記5:正則化與
  • 不到200 行代碼,教你如何用 Keras 搭建生成對抗網絡(GAN)
    它的核心思想是:同時訓練兩個相互協作、同時又相互競爭的深度神經網絡(一個稱為生成器 Generator,另一個稱為判別器 Discriminator)來處理無監督學習的相關問題。在訓練過程中,兩個網絡最終都要學習如何處理任務。通常,我們會用下面這個例子來說明 GAN 的原理:將警察視為判別器,製造假幣的犯罪分子視為生成器。一開始,犯罪分子會首先向警察展示一張假幣。
  • 菜鳥:簡單神經網絡train and test詳解(雙層)
    簡單神經網絡train and test詳解(雙層)【 The latest data : 2018/05/01 】Yuchen1. NN模型如下神經網絡整體架構內容可參考之前的雲筆記《06_神經網絡整體架構》http://note.youdao.com/noteshare?
  • 乾貨分享|使用JAX創建神經網絡的對抗性示例(附詳細代碼)
    在本教程中,我們將看到如何創建使用JAX訓練神經網絡的對抗示例。首先,讓我們看一些定義。有哪些例子?簡而言之,對抗性示例是神經網絡的輸入,這些輸入經過優化以欺騙算法,即導致目標變量分類錯誤。通過向目標變量添加「適當的」噪聲,我們可以對目標變量進行錯誤分類。下圖演示了該概念。本教程的重點是演示如何創建對抗示例。我們將使用快速梯度符號法生成。
  • 【乾貨】用神經網絡識別歌曲流派(附代碼)
    作者:Navdeep Singh編譯:肖琴【新智元導讀】本文手把手教你如何構建一個能夠識別歌曲類型的神經網絡。它們明顯不同,你可以看到它們的MFCC數值是不同的。讓我們轉到代碼(本文的所有代碼文件都可以在Github連結中找到)。
  • 代碼詳解:一文掌握神經網絡超參數調優
    該景觀類似於神經網絡的損失平面。訓練神經網絡的目的是通過某種形式的優化找到損失平面上的最小值——典型的隨機坡度減少。在學習使用高難度的優化功能後,本文讀者能充分應對施行神經網絡時遇到的實際問題場景。測試神經網絡前,首先需要給功能下定義能並找出最小值(否則無法確定為正確答案)。
  • 如何使用純NumPy代碼從頭實現簡單的卷積神經網絡!這篇文章真叼
    在這種情況下,最好自己親手構建此類模型,這可以幫助你最大程度地控制網絡。因此在本文中,我們將僅使用 NumPy 嘗試創建 CNN。我們會創建三個層,即卷積層(簡稱 conv)、ReLU 層和最大池化層。所涉及的主要步驟如下:
  • 代碼詳解:用Pytorch訓練快速神經網絡的9個技巧
    如果市面上有99個加速指南,但你可能只看過1個?(沒錯,就是這樣)。但這份終極指南,會一步步教你清除模型中所有的(GP模型)。不要讓你的神經網絡變成這樣。(圖片來源:Monsters U)這份指南的介紹從簡單到複雜,一直介紹到你可以完成的大多數PITA修改,以充分利用你的網絡。
  • 學完NumPy,直接開始玩神經網絡
    使用成熟的Tensorflow、PyTorch框架去實現遞歸神經網絡(RNN),已經極大降低了技術的使用門檻
  • 使用Python和Numpy構建神經網絡模型——波士頓房價預測案例
    希望通過本文,可以讓更多朋友了解和起步神經網絡的搭建。01案例分析很多人看到問題就敲代碼,但是我認為更重要的首先是分析問題。首先,我們先來看房價受哪些因素。(如果日後有需要完善,我再更新)在上課時,老師曾今講過,神經網絡模型的搭建有些類似當年八股文,有著高度類似的格式。圖片來自AIStudio不過呢,也正是因為深度學習的建模和訓練的過程存在通用性,在構建不同的模型時,只有模型三要素不同,其它步驟基本一致,深度學習框架才有用武之地。
  • 用純NumPy碼一個RNN、LSTM:這是最好的入門方式了
    機器之心報導參與:思源隨著 TensorFlow 和 PyTorch 等框架的流行,很多時候搭建神經網絡也就調用幾行 API 的事。大多數開發者對底層運行機制,尤其是如何使用純 NumPy 實現神經網絡變得比較陌生。
  • 驚為天人,NumPy手寫全部主流機器學習模型,代碼超3萬行
    超過 3 萬行代碼、30 多個模型,這也許能打造「最強」的機器學習基石?NumPy 作為 Python 生態中最受歡迎的科學計算包,很多讀者已經非常熟悉它了。它為 Python 提供高效率的多維數組計算,並提供了一系列高等數學函數,我們可以快速搭建模型的整個計算流程。毫不負責任地說,NumPy 就是現代深度學習框架的「爸爸」。
  • 入門| Tensorflow實戰講解神經網絡搭建詳細過程
    【IT168 技術】之前我們講了神經網絡的起源、單層神經網絡、多層神經網絡的搭建過程、搭建時要注意到的具體問題、以及解決這些問題的具體方法。本文將通過一個經典的案例:MNIST手寫數字識別,以代碼的形式來為大家梳理一遍神經網絡的整個過程。
  • 神經網絡篇——從代碼出發理解BP神經網絡
    下面小編將以代碼的方式帶著大家一步一步實現BP神經網絡,並且還會再用框架的方式實現BP網絡,用來和源碼作比較,認識一下TensorFlow2.0的強大。我們首先要理清建立BP神經網絡的目的,其次,確定BP神經網絡的結構,簡單地以一個輸入層,一個隱藏層,一個輸出層為例,我們來思考一下寫代碼的思路,然後一步一步實現。
  • 使用神經網絡為圖像生成標題
    我們都知道,神經網絡可以在執行某些任務時複製人腦的功能。神經網絡在計算機視覺和自然語言生成方面的應用已經非常引人注目。本文將介紹神經網絡的一個這樣的應用,並讓讀者了解如何使用CNNs和RNNs (LSTM)的混合網絡實際為圖像生成標題(描述)。
  • 手把手教你用TensorFlow搭建圖像識別系統(一)|...
    這只是一篇詳細描述如何開始搭建一個機器學習系統,並讓它可以識別所看到圖像的文章。作者Wolfgang Beyer目前現在正在學習人工智慧和機器學習的內容。他認為最好的學習方式不是僅僅閱讀各類材料,而是要真正地去動手搭建一個系統。這就是雷鋒網翻譯本文的目的,也是作者要向你介紹的。