秒懂神經網絡---震驚!!!神經網絡原來可以這么簡單!
一、總結
一句話總結:
神經網絡代碼編寫很容易:class+方法
神經網絡的思路也很容易:由輸入到輸出,只不過這個過程經過了一些優化
1、神經網絡解決實際問題步驟?
1、【搭建】神經網絡模型:比如搭建神經網絡基礎模塊 神經元
2、【訓練(優化)】神經網絡模型:用大量數據訓練,減少神經網絡損失,提高精確度
2、神經網絡中的 基本模塊 神經元是什么?
類似生物神經元:接受外來刺激(信號),再由細胞體傳出刺激(信號)
權重輸入+激活函數=輸出:1、輸入乘以權重; 2、加偏置; 3、經過激活函數處理;
3、神經網絡中神經元中的 激活函數 的作用是什么?
格式化輸出:輸出介於0和1:將無限制的輸入轉換為可預測形式的輸出
例如sigmoid:f(x)=e^x/(1+e^x):把 (−∞,+∞) 范圍內的數壓縮到 (0, 1)以內
4、神經網絡最簡單的神經元中,總共經過了哪三步計算?
1、輸入乘以權重
2、加偏置
3、經過激活函數處理
先將兩個輸入乘以權重(weight): x1→x1 × w1 x2→x2 × w2 把兩個結果想加,再加上一個偏置(bias): (x1 × w1)+(x2 × w2)+ b 最后將它們經過激活函數(activation function)處理得到輸出: y = f(x1 × w1 + x2 × w2 + b)
5、神經網絡最簡單的神經元中,對於每個輸入信號,它的格式是怎樣的?
由輸入屬性構成:每個輸入信號由多個輸入的屬性構成,比如輸入為圖片時:圖片像素集,圖片大小信息,圖片時間信息 等
屬性有自己的權重和偏移:信號輸入中的每個屬性,有自己的權重和偏移
實例:比如辨別圖片中動物名字:圖片像素集,圖片大小信息,圖片時間信息 等圖片屬性可以構成一個圖片輸入
6、Python數學函數庫NumPy構建簡單神經網絡代碼注意?
引入Numpy庫:import numpy as np
構建神經元類:class Neuron:,里面有__init__()方法和正反饋feedforward()方法
輸入數據:weights = np.array([0, 1]) # w1 = 0, w2 = 1 ; x = np.array([2, 3]) # x1 = 2, x2 = 3
feedforward 英 ['fiːdfɔːwəd] 美 n. 前饋(控制);正反饋 import numpy as np def sigmoid(x): # Our activation function: f(x) = 1 / (1 + e^(-x)) return 1 / (1 + np.exp(-x)) class Neuron: def __init__(self, weights, bias): self.weights = weights self.bias = bias def feedforward(self, inputs): # Weight inputs, add bias, then use the activation function total = np.dot(self.weights, inputs) + self.bias return sigmoid(total) weights = np.array([0, 1]) # w1 = 0, w2 = 1 bias = 4 # b = 4 n = Neuron(weights, bias) x = np.array([2, 3]) # x1 = 2, x2 = 3 print(n.feedforward(x)) # 0.9990889488055994
7、一個神經網絡由哪大的三層構成?
輸入層:Input Layer:
隱藏層:Hidden Layer:
輸出層:Output Layer:
8、神經網絡中的前饋(正反饋)是什么?
向前傳遞的過程:把神經元的輸入向前傳遞獲得輸出的過程
9、神經網絡的隱藏層可能有多層,那么 神經元的正反饋函數應該怎么寫?
遞歸:其實神經網絡的代碼相對還是比較簡單的
class OurNeuralNetwork: def feedforward(self, x): out_h1 = self.h1.feedforward(x) out_h2 = self.h2.feedforward(x) # The inputs for o1 are the outputs from h1 and h2 out_o1 = self.o1.feedforward(np.array([out_h1, out_h2])) return out_o1
10、我們怎么才算把我們的神經網絡模型設計好了?
測試結果小於 設定的誤差值:我們設定了一個設定的誤差值,如果測試結果的數據小於設定的誤差值,我們可以看做神經網絡模型搭建好了
11、訓練神經網絡的過程的 實質 是什么?
優化模型的過程:其實也就是讓損失函數的結果最小
12、如果我們用神經網絡根據某人的身高和體重來推測其性別,那么輸入是什么?
一個人的信息即為一條輸入信號
每個輸入信號分為多個屬性:比如a的身高,a的體重就是a這個人對應的輸入信號的兩個屬性(或者叫分支)
13、我們如何衡量一個神經網絡模型的好壞?
用Loss Function(損失函數):最簡單的損失函數比如 均方誤差(MSE)
import numpy as np def mse_loss(y_true, y_pred): # y_true and y_pred are numpy arrays of the same length. return ((y_true - y_pred) ** 2).mean() y_true = np.array([1, 0, 0, 1]) y_pred = np.array([0, 0, 0, 0]) print(mse_loss(y_true, y_pred)) # 0.5
14、我們比如用 均方誤差 來衡量一個神經網絡模型的好壞,那么 均方誤差 是什么?
所有數據方差的平均值:我們不妨就把它定義為損失函數。預測結果越好,損失就越低,訓練神經網絡就是將損失最小化。
15、神經網絡中那種錯綜復雜的上傳到下層的線如何實現?
屬性的權重和:因為每一個下層節點得到的是一整套的輸入,而不是某個輸入的屬性
16、如果調整一下w1,損失函數是會變大還是變小?
偏導數∂L/∂w1:偏導數∂L/∂w1是正是負能回答這個問題:當∂L/∂w1是正數時,w1會變小;當∂L/∂w1是負數 時,w1會變大。
17、如何求一個函數的導數,比如 偏導數∂L/∂w1?
自變量降次:在函數中,把∂w1看成自變量,然后對∂w1進行降次處理,相當於一次函數求斜率
18、神經網絡中,如何求 輸出結果對某一個權重的偏倒數,比如Ypred(最終結果)對w1(輸入信號的屬性第一個的權重)?
鏈式展開:Ypred對w1的偏導=Ypred對h1的偏導 * h1對w1的偏導
19、神經網絡中的優化神經網絡中的 隨機梯度下降 是干嘛的?
優化神經網絡:無非就是優化【權重 和 偏移】
優化權重:隨機梯度下降是用來優化權重的
w1=w1-η·∂L/∂w1:看這個函數就特別清晰隨機梯度下降是干嘛的以及它的特性有哪些
20、隨機梯度下降(w1=w1-η·∂L/∂w1)中的η是干嘛的?
學習率:訓練網絡快慢:η是一個常數,稱為學習率(learning rate),它決定了我們訓練網絡速率的快慢。
21、隨機梯度下降(w1=w1-η·∂L/∂w1)的特性是什么?
將w1減去η·∂L/∂w1,就等到了新的權重w1。
當∂L/∂w1是正數時,w1會變小;當∂L/∂w1是負數 時,w1會變大。
22、神經網絡的 偽代碼 如何書寫?
類+方法:就是一個神經網絡的類,里面有【初始化,正反饋,負反饋等】幾個方法
23、神經網絡的學習進階?
1、強大的庫:用更大更好的機器學習庫搭建神經網絡,如Tensorflow、Keras、PyTorch
2、可視化:在瀏覽器中的直觀理解神經網絡:https://playground.tensorflow.org/
3、激活函數:學習sigmoid以外的其他激活函數:https://keras.io/activations/
4、優化器:學習SGD以外的其他優化器:https://keras.io/optimizers/
1、用更大更好的機器學習庫搭建神經網絡,如Tensorflow、Keras、PyTorch 2、在瀏覽器中的直觀理解神經網絡:https://playground.tensorflow.org/ 3、學習sigmoid以外的其他激活函數:https://keras.io/activations/ 4、學習SGD以外的其他優化器:https://keras.io/optimizers/ 5、學習卷積神經網絡(CNN) 6、學習遞歸神經網絡(RNN)
二、非常簡單清晰的神經網絡教程(轉)
轉自:AI之城
https://mp.weixin.qq.com/s/GQyXEdURHixd8jrf9A197A
“我在網上看到過很多神經網絡的實現方法,但這一篇是最簡單、最清晰的。”
一位來自普林斯頓的華人小哥Victor Zhou,寫了篇神經網絡入門教程,在線代碼網站Repl.it聯合創始人Amjad Masad看完以后,給予如是評價。
這篇教程發布僅天時間,就在Hacker News論壇上收獲了574贊。程序員們紛紛誇贊這篇文章的代碼寫得很好,變量名很規范,讓人一目了然。
下面就讓我們一起從零開始學習神經網絡吧。
實現方法
搭建基本模塊——神經元
在說神經網絡之前,我們討論一下神經元(Neurons),它是神經網絡的基本單元。神經元先獲得輸入,然后執行某些數學運算后,再產生一個輸出。比如一個2輸入神經元的例子:
在這個神經元中,輸入總共經歷了3步數學運算,
先將兩個輸入乘以權重(weight):
x1→x1 × w1
x2→x2 × w2
把兩個結果想加,再加上一個偏置(bias):
(x1 × w1)+(x2 × w2)+ b
最后將它們經過激活函數(activation function)處理得到輸出:
y = f(x1 × w1 + x2 × w2 + b)
激活函數的作用是將無限制的輸入轉換為可預測形式的輸出。一種常用的激活函數是sigmoid函數:
sigmoid函數的輸出介於0和1,我們可以理解為它把 (−∞,+∞) 范圍內的數壓縮到 (0, 1)以內。正值越大輸出越接近1,負向數值越大輸出越接近0。
舉個例子,上面神經元里的權重和偏置取如下數值:
w=[0,1]
b = 4
w=[0,1]是w1=0、w2=1的向量形式寫法。給神經元一個輸入x=[2,3],可以用向量點積的形式把神經元的輸出計算出來:
w·x+b =(x1 × w1)+(x2 × w2)+ b = 0×2+1×3+4=7
y=f(w⋅X+b)=f(7)=0.999
以上步驟的Python代碼是:
import numpy as np
def sigmoid(x):
# Our activation function: f(x) = 1 / (1 + e^(-x))
return 1 / (1 + np.exp(-x))
class Neuron:
def __init__(self, weights, bias):
self.weights = weights
self.bias = bias
def feedforward(self, inputs):
# Weight inputs, add bias, then use the activation function
total = np.dot(self.weights, inputs) + self.bias
return sigmoid(total)
weights = np.array([0, 1]) # w1 = 0, w2 = 1
bias = 4 # b = 4
n = Neuron(weights, bias)
x = np.array([2, 3]) # x1 = 2, x2 = 3
print(n.feedforward(x)) # 0.9990889488055994
我們在代碼中調用了一個強大的Python數學函數庫NumPy。
搭建神經網絡
神經網絡就是把一堆神經元連接在一起,下面是一個神經網絡的簡單舉例:
這個網絡有2個輸入、一個包含2個神經元的隱藏層(h1和h2)、包含1個神經元的輸出層o1。
隱藏層是夾在輸入輸入層和輸出層之間的部分,一個神經網絡可以有多個隱藏層。
把神經元的輸入向前傳遞獲得輸出的過程稱為前饋(feedforward)。
我們假設上面的網絡里所有神經元都具有相同的權重w=[0,1]和偏置b=0,激活函數都是sigmoid,那么我們會得到什么輸出呢?
h1=h2=f(w⋅x+b)=f((0×2)+(1×3)+0)
=f(3)
=0.9526
o1=f(w⋅[h1,h2]+b)=f((0∗h1)+(1∗h2)+0)
=f(0.9526)
=0.7216
以下是實現代碼:
import numpy as np
# ... code from previous section here
class OurNeuralNetwork:
'''
A neural network with:
- 2 inputs
- a hidden layer with 2 neurons (h1, h2)
- an output layer with 1 neuron (o1)
Each neuron has the same weights and bias:
- w = [0, 1]
- b = 0
'''
def __init__(self):
weights = np.array([0, 1])
bias = 0
# The Neuron class here is from the previous section
self.h1 = Neuron(weights, bias)
self.h2 = Neuron(weights, bias)
self.o1 = Neuron(weights, bias)
def feedforward(self, x):
out_h1 = self.h1.feedforward(x)
out_h2 = self.h2.feedforward(x)
# The inputs for o1 are the outputs from h1 and h2
out_o1 = self.o1.feedforward(np.array([out_h1, out_h2]))
return out_o1
network = OurNeuralNetwork()
x = np.array([2, 3])
print(network.feedforward(x)) # 0.7216325609518421
訓練神經網絡
現在我們已經學會了如何搭建神經網絡,現在我們來學習如何訓練它,其實這就是一個優化的過程。
假設有一個數據集,包含4個人的身高、體重和性別:
現在我們的目標是訓練一個網絡,根據體重和身高來推測某人的性別。
為了簡便起見,我們將每個人的身高、體重減去一個固定數值,把性別男定義為1、性別女定義為0。
在訓練神經網絡之前,我們需要有一個標准定義它到底好不好,以便我們進行改進,這就是損失(loss)。
比如用均方誤差(MSE)來定義損失:
n是樣本的數量,在上面的數據集中是4;
y代表人的性別,男性是1,女性是0;
ytrue是變量的真實值,ypred是變量的預測值。
顧名思義,均方誤差就是所有數據方差的平均值,我們不妨就把它定義為損失函數。預測結果越好,損失就越低,訓練神經網絡就是將損失最小化。
如果上面網絡的輸出一直是0,也就是預測所有人都是男性,那么損失是:
MSE= 1/4 (1+0+0+1)= 0.5
計算損失函數的代碼如下:
import numpy as np
def mse_loss(y_true, y_pred):
# y_true and y_pred are numpy arrays of the same length.
return ((y_true - y_pred) ** 2).mean()
y_true = np.array([1, 0, 0, 1])
y_pred = np.array([0, 0, 0, 0])
print(mse_loss(y_true, y_pred)) # 0.5
減少神經網絡損失
這個神經網絡不夠好,還要不斷優化,盡量減少損失。我們知道,改變網絡的權重和偏置可以影響預測值,但我們應該怎么做呢?
為了簡單起見,我們把數據集縮減到只包含Alice一個人的數據。於是損失函數就剩下Alice一個人的方差:
預測值是由一系列網絡權重和偏置計算出來的:
所以損失函數實際上是包含多個權重、偏置的多元函數:
(注意!前方高能!需要你有一些基本的多元函數微分知識,比如偏導數、鏈式求導法則。)
如果調整一下w1,損失函數是會變大還是變小?我們需要知道偏導數∂L/∂w1是正是負才能回答這個問題。
根據鏈式求導法則:
而L=(1-ypred)2,可以求得第一項偏導數:
接下來我們要想辦法獲得ypred和w1的關系,我們已經知道神經元h1、h2和o1的數學運算規則:
實際上只有神經元h1中包含權重w1,所以我們再次運用鏈式求導法則:
然后求∂h1/∂w1
我們在上面的計算中遇到了2次激活函數sigmoid的導數f′(x),sigmoid函數的導數很容易求得:
總的鏈式求導公式:
這種向后計算偏導數的系統稱為反向傳播(backpropagation)。
上面的數學符號太多,下面我們帶入實際數值來計算一下。h1、h2和o1
h1=f(x1⋅w1+x2⋅w2+b1)=0.0474
h2=f(w3⋅x3+w4⋅x4+b2)=0.0474
o1=f(w5⋅h1+w6⋅h2+b3)=f(0.0474+0.0474+0)=f(0.0948)=0.524
神經網絡的輸出y=0.524,沒有顯示出強烈的是男(1)是女(0)的證據。現在的預測效果還很不好。
我們再計算一下當前網絡的偏導數∂L/∂w1:
這個結果告訴我們:如果增大w1,損失函數L會有一個非常小的增長。
隨機梯度下降
下面將使用一種稱為隨機梯度下降(SGD)的優化算法,來訓練網絡。
經過前面的運算,我們已經有了訓練神經網絡所有數據。但是該如何操作?SGD定義了改變權重和偏置的方法:
η是一個常數,稱為學習率(learning rate),它決定了我們訓練網絡速率的快慢。將w1減去η·∂L/∂w1,就等到了新的權重w1。
當∂L/∂w1是正數時,w1會變小;當∂L/∂w1是負數 時,w1會變大。
如果我們用這種方法去逐步改變網絡的權重w和偏置b,損失函數會緩慢地降低,從而改進我們的神經網絡。
訓練流程如下:
1、從數據集中選擇一個樣本;
2、計算損失函數對所有權重和偏置的偏導數;
3、使用更新公式更新每個權重和偏置;
4、回到第1步。
我們用Python代碼實現這個過程:
import numpy as np
def sigmoid(x):
# Sigmoid activation function: f(x) = 1 / (1 + e^(-x))
return 1 / (1 + np.exp(-x))
def deriv_sigmoid(x):
# Derivative of sigmoid: f'(x) = f(x) * (1 - f(x))
fx = sigmoid(x)
return fx * (1 - fx)
def mse_loss(y_true, y_pred):
# y_true and y_pred are numpy arrays of the same length.
return ((y_true - y_pred) ** 2).mean()
class OurNeuralNetwork:
'''
A neural network with:
- 2 inputs
- a hidden layer with 2 neurons (h1, h2)
- an output layer with 1 neuron (o1)
*** DISCLAIMER ***:
The code below is intended to be simple and educational, NOT optimal.
Real neural net code looks nothing like this. DO NOT use this code.
Instead, read/run it to understand how this specific network works.
'''
def __init__(self):
# Weights
self.w1 = np.random.normal()
self.w2 = np.random.normal()
self.w3 = np.random.normal()
self.w4 = np.random.normal()
self.w5 = np.random.normal()
self.w6 = np.random.normal()
# Biases
self.b1 = np.random.normal()
self.b2 = np.random.normal()
self.b3 = np.random.normal()
def feedforward(self, x):
# x is a numpy array with 2 elements.
h1 = sigmoid(self.w1 * x[0] + self.w2 * x[1] + self.b1)
h2 = sigmoid(self.w3 * x[0] + self.w4 * x[1] + self.b2)
o1 = sigmoid(self.w5 * h1 + self.w6 * h2 + self.b3)
return o1
def train(self, data, all_y_trues):
'''
- data is a (n x 2) numpy array, n = # of samples in the dataset.
- all_y_trues is a numpy array with n elements.
Elements in all_y_trues correspond to those in data.
'''
learn_rate = 0.1
epochs = 1000 # number of times to loop through the entire dataset
for epoch in range(epochs):
for x, y_true in zip(data, all_y_trues):
# --- Do a feedforward (we'll need these values later)
sum_h1 = self.w1 * x[0] + self.w2 * x[1] + self.b1
h1 = sigmoid(sum_h1)
sum_h2 = self.w3 * x[0] + self.w4 * x[1] + self.b2
h2 = sigmoid(sum_h2)
sum_o1 = self.w5 * h1 + self.w6 * h2 + self.b3
o1 = sigmoid(sum_o1)
y_pred = o1
# --- Calculate partial derivatives.
# --- Naming: d_L_d_w1 represents "partial L / partial w1"
d_L_d_ypred = -2 * (y_true - y_pred)
# Neuron o1
d_ypred_d_w5 = h1 * deriv_sigmoid(sum_o1)
d_ypred_d_w6 = h2 * deriv_sigmoid(sum_o1)
d_ypred_d_b3 = deriv_sigmoid(sum_o1)
d_ypred_d_h1 = self.w5 * deriv_sigmoid(sum_o1)
d_ypred_d_h2 = self.w6 * deriv_sigmoid(sum_o1)
# Neuron h1
d_h1_d_w1 = x[0] * deriv_sigmoid(sum_h1)
d_h1_d_w2 = x[1] * deriv_sigmoid(sum_h1)
d_h1_d_b1 = deriv_sigmoid(sum_h1)
# Neuron h2
d_h2_d_w3 = x[0] * deriv_sigmoid(sum_h2)
d_h2_d_w4 = x[1] * deriv_sigmoid(sum_h2)
d_h2_d_b2 = deriv_sigmoid(sum_h2)
# --- Update weights and biases
# Neuron h1
self.w1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w1
self.w2 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w2
self.b1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_b1
# Neuron h2
self.w3 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h2_d_w3
self.w4 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h2_d_w4
self.b2 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h2_d_b2
# Neuron o1
self.w5 -= learn_rate * d_L_d_ypred * d_ypred_d_w5
self.w6 -= learn_rate * d_L_d_ypred * d_ypred_d_w6
self.b3 -= learn_rate * d_L_d_ypred * d_ypred_d_b3
# --- Calculate total loss at the end of each epoch
if epoch % 10 == 0:
y_preds = np.apply_along_axis(self.feedforward, 1, data)
loss = mse_loss(all_y_trues, y_preds)
print("Epoch %d loss: %.3f" % (epoch, loss))
# Define dataset
data = np.array([
[-2, -1], # Alice
[25, 6], # Bob
[17, 4], # Charlie
[-15, -6], # Diana
])
all_y_trues = np.array([
1, # Alice
0, # Bob
0, # Charlie
1, # Diana
])
# Train our neural network!
network = OurNeuralNetwork()
network.train(data, all_y_trues)
隨着學習過程的進行,損失函數逐漸減小。
現在我們可以用它來推測出每個人的性別了:
# Make some predictions
emily = np.array([-7, -3]) # 128 pounds, 63 inches
frank = np.array([20, 2]) # 155 pounds, 68 inches
print("Emily: %.3f" % network.feedforward(emily)) # 0.951 - F
print("Frank: %.3f" % network.feedforward(frank)) # 0.039 - M
更多
這篇教程只是萬里長征第一步,后面還有很多知識需要學習:
1、用更大更好的機器學習庫搭建神經網絡,如Tensorflow、Keras、PyTorch
2、在瀏覽器中的直觀理解神經網絡:https://playground.tensorflow.org/
3、學習sigmoid以外的其他激活函數:https://keras.io/activations/
4、學習SGD以外的其他優化器:https://keras.io/optimizers/
5、學習卷積神經網絡(CNN)
6、學習遞歸神經網絡(RNN)