單隱層前饋神經網路
這篇部落格主要介紹神經網路基礎,單隱層前饋神經網路與反向傳播演算法。
神經網路故名思議是由人的神經系統啟發而得來的一種模型。神經網路可以用來做分類和迴歸等任務,其具有很好的非線性擬合能力。接下來我們就來詳細介紹一下但隱層前饋神經網路。
首先我們來看一下神經元的數學模型,如下圖所示:
可以看到為輸入訊號,而神經元最終輸出為,由此我們可以看到,單個神經元是多輸入單輸出的。但是從上圖我們可以看到,有輸入到輸出中間還經歷了一些步驟,這些步驟在神經網路中是非常關鍵的,因為正是有了中間這些步驟,一個神經網路才能夠真正的具有了非線性擬合能力,這是為什麼呢?接下來就給出解釋。
從上圖中輸入
到這一步還沒有使得神經網路具有非線性擬合能力,因為正如我們所看到的,所有的操作對於輸入來說都是線性的,使得神經網路具有非線性擬合能力的一步是下一步操作,如上圖中啟用函式處所示,我們將線性加權求和得到的輸出輸入到了一個叫做啟用函式的東西里面,那麼啟用函式又是什麼呢?實際上啟用函式就是一個非線性的一個函式,如上圖中就是啟用函式,那麼常用的啟用函式有以下幾種:
對於前兩個啟用函式,sigmoid啟用函式將輸入值壓縮到了0到1之間,但其缺點是存在梯度消失的問題,如下圖所示:
可以看到當sigmoid的輸入很大或很小時,其梯度幾乎趨近於0。
再來看tanh啟用函式,如下圖所示:
由上圖可以看出,tanh啟用函式和sigmoid啟用函式非常相似,只不過tanh啟用函式將輸入值壓縮到了-1到1之間,由其梯度影象可以看出,當輸入很大或很小時梯度也是趨於0的。
最後讓我們來看一下relu啟用函式,如下圖所示:
從上圖中可以看出,relu啟用函式在0到正無窮上會隨著輸入的增大而無限增大,在小於0的區間上其值全為0,但是我們還可以看到其梯度在0到正無窮上永遠為1。
由於神經網路最後對於損失函式的優化也是使用梯度下降,因此在實際運用中,我們基本上會經常用relu啟用函式,因為sigmoid啟用函式以及tanh啟用函式從上面的影象中已經可以看出,其存在梯度消失的問題,我們知道,當我們使用梯度下降去優化某個損失函式時,是需要求梯度的,但是如果輸入過大(可能初始化權重過大,也可能特徵過大)並且採取的是sigmoid或tanh啟用函式,那麼當我們在輸入位置求取梯度時其梯度值趨近於0,而我們對於引數的更新量也趨近於0,因此最終會因為梯度消失的問題造成收斂過慢,因此我們會使用relu啟用函式來加快收斂速度,但relu啟用函式的精度不如sigmoid啟用函式。
接下來我們看一下單隱層前饋神經網路一般結構,如下圖:
其中表示第m個樣本的第n個特徵,可以看到輸入層神經元神經元個數應該和一個樣本的特徵數一樣,而表示第m個樣本的第k個輸出,通常情況下,如果這是一個分類問題則,如果是迴歸問題則。
下面舉一個結構比較簡單的網路來說明但隱層前饋網路的工作機理,網路結構如下圖:
上圖中表示第層第i個神經元和第層的第j個神經元連結權重。則隱層神經元輸出計算公式如下:
其中表示第i隱層的第j個神經元的輸出。而表示第i隱層神經元輸出時所用的啟用函式,表示第i隱層的第j個神經元的計算線性加權求和時所用的偏置,由於是但隱層神經網路,所以上述所說的第i隱層就可以直接理解為隱層即可。
而輸出層神經元的輸出計算公式如下:
當我們求得輸出之後就能夠計算輸出與真實之之間的差距,我們稱為損失,我們往往是送入一批資料,所以會求得一批損失,這裡我們以迴歸問題為例求取均方誤差:
其中為網路輸出,而為樣本真實標記。
當我們求得損失函式後,我們就可以對各個引數求偏導計算梯度,進而進行梯度下降來優化損失函式。
注意如果是迴歸問題最後輸出層不進行啟用函式的啟用操作,因為如果採用sigmoid或tanh啟用函式啟用後輸出永遠在0到1區間內或-1到1區間內,而如果採取relu啟用函式啟用,則輸出捨棄了負值的那一部分,這樣可能永遠都不能很好的擬合真實值;而如果是分類問題便可以使用啟用函式,並且如果是分類問題,我們的輸出層神經元個數同類別數目是一樣的,並且我們首先需要將樣本標記進行one_hot編碼,使每個樣本標記是一個概率分佈,即某個類別的概率為1。我們網路輸出之後需要進行一個softmax將網路輸出轉化為一個概率分佈,而我們的損失是求取網路輸出的概率分佈和真實樣本標記的交叉熵(交叉熵可以衡量兩個概率分佈之間的距離,我們希望真實的概率分佈和網路輸出的概率分佈距離越小越好),同樣我們往往是求取一批樣本的交叉熵然後取均值作為損失函式,之後同迴歸問題一樣對其進行梯度下降優化即可。
以下是本人用python以及tensorflow庫編寫的但隱層神經網路供大家參考:
# coding: utf-8
# In[1]:
import numpy as np
from numpy import random as rd
import matplotlib.pyplot as plt
import tensorflow as tf
# In[2]:
# 構造樣本點
x = np.linspace(-5, 5, 100)
# 對x做了個升維,樣本變成了[x, x**2, x**3]
x = np.concatenate([x.reshape(-1, 1), np.power(x.reshape(-1, 1), 2),
np.power(x.reshape(-1, 1), 3)], axis=1)
y = (np.sin(x[:, 0]) + rd.uniform(-0.2, 0.2, 100)).reshape(-1, 1)
# unit_count_of_hidden_layer是指定隱層神經元個數的,max_train_step表示訓練多少次,
# learning_rate表示梯度下降的學習率
unit_count_of_hidden_layer = 10
max_train_step = 2000
learning_rate = 0.1
# In[3]:
def add_layer(layer_number, input_value, unit_count, activation_function):
# 用來新增隱層和輸出層
# input_value是隱層的輸入
# unit_count是隱層的神經元個數
# activation_function指定啟用函式:tf.nn.relu或tf.nn.sigmoid或tf.nn.tanh
with tf.variable_scope("layer_%d" % layer_number, reuse=tf.AUTO_REUSE):
weight = tf.get_variable(shape=[input_value.shape[-1], unit_count],
dtype=tf.float32, initializer=tf.random_normal_initializer(mean=1, stddev=1),
name="weights", trainable=True)
bias = tf.get_variable(shape=[1, unit_count], dtype=tf.float32, initializer=tf.constant_initializer(0),
name="bias", trainable=True)
matmul_add_bias_result = tf.add(tf.matmul(input_value, weight), bias, name="matmul_add_bias")
output = matmul_add_bias_result
if activation_function:
output = activation_function(matmul_add_bias_result)
return output
def demo():
x_placeholder = tf.placeholder(shape=[100, x.shape[-1]], dtype=tf.float32, name="x_input")
y_placeholder = tf.placeholder(shape=[100, 1], dtype=tf.float32, name="y_input")
hidden_layer_output = add_layer(1, x_placeholder, unit_count_of_hidden_layer, tf.nn.sigmoid)
y_output = add_layer(2, hidden_layer_output, 1, None)
loss = tf.reduce_mean(tf.square(y_output - y_placeholder))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss)
init_op = tf.global_variables_initializer()
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:
sess.run(init_op)
for i in range(1, 1 + max_train_step):
_, loss_, y_output_ = sess.run(fetches=[train_op, loss, y_output],
feed_dict={x_placeholder: x, y_placeholder: y})
if i % int(0.1 * max_train_step) == 0:
print("第%d次迭代損失:%f" % (i, loss_))
return y_output_
# In[4]:
y_predict = demo()
# In[5]:
# 畫圖
fig = plt.figure(figsize=(8, 6))
ax = plt.subplot(1, 1, 1)
ax.scatter(x[:, 0], y, color="r", label="samples")
ax.plot(x[:, 0], y_predict, linewidth=1, label="predict", color="b")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend(loc="upper right")
plt.show()