python實作一個簡單三層神經網絡的搭建(有代碼)
廢話不多說了,直接步入正題,一個完整的神經網絡一般由三層構成:輸入層,隐藏層(可以有多層)和輸出層。本文所建構的神經網絡隐藏層隻有一層。一個神經網絡主要由三部分構成(代碼結構上):初始化,訓練,和預測。首先我們先來初始化這個神經網絡吧!
1.初始化
- 我們所要初始化的内容包括:神經網絡每層上的神經元個數(這個是根據實際問題輸入輸出而得到的,我們将它設定為一個可自定義量)。
- 不同層間資料互相傳送的權重值。
- 激活函數(模拟自然界的神經元,刺激信号需要達到一定的程度才能激活神經元)
下面上代碼:
def __init__(self, input_nodes_num, hidden_nodes_num, output_nodes_num, lr):
# 初始化神經元個數,可以直接修改
self.input_nodes = input_nodes_num
self.hidden_nodes = hidden_nodes_num
self.output_nodes = output_nodes_num
self.learning_rate = lr
# 初始化權重值,利用正态分布函數進行随機初始化,均值為0,方差為神經元個數開方
self.w_input_hidden = numpy.random.normal(0.0, pow(self.hidden_nodes, -0.5),
(self.hidden_nodes, self.input_nodes))
self.w_hidden_output = numpy.random.normal(0.0, pow(self.output_nodes, -0.5),
(self.output_nodes, self.hidden_nodes))
# 初始化激活函數,激活函數選用Sigmoid函數,更加平滑,接近自然界的神經元行為模式
# lambda定義了一個匿名函數
self.activation_function = lambda x: scipy.special.expit(x)
pass
下面我們來解釋一下上述代碼段中的一些程式設計知識。首先是__init__()它是一個類的構造函數,在建構一個類的對象時會調用此函數,是以我們将神經網絡初始化相關代碼放到這個函數裡。
self.w_input_hidden = numpy.random.normal(0.0, pow(self.hidden_nodes, -0.5),
(self.hidden_nodes, self.input_nodes))
這句代碼使用了numpy庫中的random.normal()函數,為輸入層和隐藏層之間的資料傳遞初始化了權重值,這個函數會根據正态分布随機生成一個
self.hidden_nodes*self.input_nodes的矩陣(hidden_nodes和input_nodes表示隐藏層和輸入層神經元的個數)。
self.activation_function = lambda x: scipy.special.expit(x)
這句代碼使用lambda定義了一個匿名函數,将它指派給激活函數,函數為sigmoid函數,是一條平滑的曲線,比較接近自然界神經元對于刺激信号的反應方式。
2.預測
按照正常順序,初始化完成後應該進行訓練,但由于訓練較為複雜,且預測較為簡單容易實作,我們先完成這一部分的代碼。預測環節需要我們将輸入資訊進行處理,權重求和後傳輸給隐藏層神經元,經過激活函數并再次權重求和後,傳輸給輸出層經過輸出層神經元的處理得到最終的結果。代碼片段如下:
def query(self, inputs_list):
# 轉置将行向量轉成列向量,将每組資料更好的分隔開來,友善後續矩陣點乘操作
inputs = np.array(inputs_list, ndmin=2).T
# 權重求和後經過sigmoid函數得到隐藏層輸出
hidden_inputs = np.dot(self.w_input_hidden, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
# 權重求和後經過sigmoid函數得到最終輸出
final_inputs = np.dot(self.w_hidden_output, hidden_outputs)
final_outputs = self.activation_function(final_inputs)
# 得到輸出資料列
return final_outputs
這段代碼沒有什麼好說的,比較簡單,隻需按照筆者上述的步驟做即可。有什麼不懂的可以看注釋或者留下評論。
3.訓練
神經網絡的訓練問題較為複雜,涉及到神經網絡的正向和反向傳播,微積分的鍊式法則,矩陣運算,偏微分求導和梯度下降算法的一些知識,都是機器學習的一些基礎知識,在這裡就不做過多的贅述,過幾天我會新發一篇詳細講一下。下面來了解一下訓練代碼段的主要任務:
- 訓練和預測一樣都要首先讀入一些輸入并預測輸出,不同的是,訓練階段我們是從訓練資料集中擷取資料,我們知道正确的輸出是什麼,而預測階段我們隻知道輸入而輸出需要通過我們訓練的模型預測出來。首先訓練階段讀入輸入并按照目前的模型對其進行預測。
- 基于訓練預測結果和标注好的實際結果的誤差更新各個層之間的權值。
下面來貼代碼:
def train(self, inputs_list, targets_list):
# 将訓練集和測試集中的資料轉化為列向量
inputs = np.array(inputs_list, ndmin=2).T
targets = np.array(targets_list, ndmin=2).T
# 隐藏層的輸入為訓練集與權重值的點乘,輸出為激活函數的輸出
hidden_inputs = np.dot(self.w_input_hidden, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
# 輸出層的輸入為隐藏層的輸出,輸出為最終結果
final_inputs = np.dot(self.w_hidden_output, hidden_outputs)
final_outputs = self.activation_function(final_inputs)
# 損失函數
output_errors = targets - final_outputs
# 隐藏層的誤差為權值矩陣的轉置與輸出誤差的點乘
hidden_errors = np.dot(self.w_hidden_output.T, output_errors)
# 對權值進行更新
self.w_hidden_output += self.learning_rate * np.dot((output_errors *
final_outputs * (1.0 - final_outputs)),
np.transpose(hidden_outputs))
self.w_input_hidden += self.learning_rate * np.dot((hidden_errors *
hidden_outputs * (1.0 - hidden_outputs)),
np.transpose(inputs))
上述代碼段可能對于一些剛接觸機器學習或深度學習的同學來說可能有點不知所雲或産生一種好複雜的感覺,但是這隻是對反向傳播算法,鍊式法則和偏導的綜合應用。我會在另一篇随筆中講述我的心得(可能講得不好),感興趣的可以看一下。
4.測試
三層神經網絡建構完成,我用mnist訓練集和測試集對其進行了測試,代碼及結果如下:
# 初始化各層神經元個數,期中輸入神經元個數取決于讀入的因變量,而輸出神經元個數取決于分類的可能性個數
input_nodes = 784
hidden_nodes = 100
output_nodes = 10
# 學習率,每次調整步幅大小
learning_rate = 0.2
n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
# 擷取訓練集資訊
training_data_file = open(\'data/mnist_train.csv\', \'r\')
training_data_list = training_data_file.readlines()
training_data_file.close()
for record in training_data_list:
all_values = record.split(\',\')
inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
targets = numpy.zeros(output_nodes) + 0.01
targets[int(all_values[0])] = 0.99
n.train(inputs, targets)
pass
print(\'train successful!\')
test_file = open(\'data/mnist_test.csv\', \'r\')
test_list = test_file.readlines()
test_file.close()
m = np.size(test_list)
j = 0.0
for record in test_list:
test_values = record.split(\',\')
np.asfarray(test_values)
results = n.query(np.asfarray(test_values[1:]))
if results[int(test_values[0])] == max(results):
j += 1
pass
print("正确率為;" + str(j/m))

覺得此篇文章有用的給筆者留個點贊吖\(^o^)/~
有什麼不懂的或者代碼資源評論區留言\(^o^)/~