構建兩層以上BP神經網路(python程式碼)

半成品文藝青年發表於2020-11-26

看了這篇部落格的手寫bp神經網路,使用了之後覺得不錯,然後想實現兩層以上的bp網路,於是自己將部分函式改寫,只要這行函式中更改列表中數字的個數就可以實現訓練

self.setup(2, [2,2,2], 1)	#入藥實現四層可改成[2,2,2,2]

如果需要看原理的可以調至上述部落格,程式碼有些冗餘希望見諒

# -*- coding: utf-8 -*-
"""
Created on Tue May 15 16:39:55 2018
@author: Administrator
"""

import math
import random
import numpy
import matplotlib.pyplot as plt
import pandas
from sklearn.preprocessing import MinMaxScaler

random.seed(0)  # 使random函式每一次生成的隨機數值都相等


def rand(a, b):
    return (b - a) * random.random() + a  # 生成a到b的隨機數


def make_matrix(m, n, fill=0.0):  # 建立一個指定大小的矩陣
    mat = []
    for i in range(m):
        mat.append([fill] * n)
    return mat


# 定義tanh函式和它的導數
def tanh(x):
    return numpy.tanh(x)


def tanh_derivate(x):
    return 1 - numpy.tanh(x) * numpy.tanh(x)  # tanh函式的導數


class BPNeuralNetwork:
    def __init__(self):  # 初始化變數
        self.output_n = 0
        self.output_n = 0
        self.input_n = 0
        self.input_cells = []
        self.output_cells = []
        self.input_weights = []
        self.output_weights = []
        self.input_correction = []
        self.output_correction = []

    def setup(self, ni, nh, no):
        self.hidden_layers = len(nh)
        self.hidden_n = [0] * self.hidden_layers  # 也就是需要初始化多少
        self.hidden_cells = [None] * self.hidden_layers
        self.hidden_weights = [None] * (self.hidden_layers - 1)
        self.hidden_correction = [None] * (self.hidden_layers - 1)
        self.input_n = ni + 1  # 輸入層+偏置項
        self.hidden_n = nh  # 一個列表
        self.h_b = [None] * self.hidden_layers
        for i in range(self.hidden_layers):
            self.hidden_n[i] += 1
        self.output_n = no  # 輸出層

        # 初始化神經元
        self.input_cells = [1.0] * self.input_n
        for i in range(self.hidden_layers):  # 也就是使用
            self.hidden_cells[i] = [1.0] * self.hidden_n[i]  # 也就是進行
        self.output_cells = [1.0] * self.output_n  #

        # 初始化連線邊的邊權
        self.input_weights = make_matrix(self.input_n, self.hidden_n[0])  # 鄰接矩陣存邊權:輸入層->隱藏層
        for i in range(self.hidden_layers - 1):
            self.hidden_weights[i] = make_matrix(self.hidden_n[i], self.hidden_n[i + 1])

        self.output_weights = make_matrix(self.hidden_n[-1], self.output_n)  # 鄰接矩陣存邊權:隱藏層->輸出層
        # 初始化bias

        for i in range(self.hidden_layers):  #
            self.h_b[i] = make_matrix(self.hidden_n[i], 1)  # 也就是使用
        self.o_b = make_matrix(self.output_n, 1)  #
        # 隨機初始化邊權:為了反向傳導做準備--->隨機初始化的目的是使對稱失效
        for i in range(self.input_n):
            for h in range(self.hidden_n[0]):
                self.input_weights[i][h] = rand(-1, 1)  # 由輸入層第i個元素到隱藏層1第j個元素的邊權為隨機值
        for k in range(self.hidden_layers - 1):
            for i in range(self.hidden_n[k]):  # 也就是使用了
                for h in range(self.hidden_n[k + 1]):
                    self.hidden_weights[k][i][h] = rand(-1, 1)  # 由隱藏層1第i個元素到隱藏層2第j個元素的邊權為隨機值
                    print(self.hidden_weights[k][i][h])
        for h in range(self.hidden_n[self.hidden_layers - 1]):
            for o in range(self.output_n):
                self.output_weights[h][o] = rand(-1, 1)  # 由隱藏層2第i個元素到輸出層第j個元素的邊權為隨機值
        # 隨機初始化bias
        for k in range(self.hidden_layers):
            for i in range(self.hidden_n[k]):  # 也就是進行
                self.h_b[k][i] = rand(-1, 1)  # dui
        for i in range(self.output_n):
            self.o_b[i] = rand(-1, 1)
        # 儲存校正矩陣,為了以後誤差做調整
        self.input_correction = make_matrix(self.input_n, self.hidden_n[0])
        for i in range(self.hidden_layers - 1):  # 就是會長生多個
            self.hidden_correction[i] = make_matrix(self.hidden_n[i], self.hidden_n[i + 1])
        self.output_correction = make_matrix(self.hidden_n[-1], self.output_n)

        # 輸出預測值

    def predict(self, inputs):
        # 對輸入層進行操作轉化樣本
        for i in range(self.input_n - 1):
            self.input_cells[i] = inputs[i]  # n個樣本從0~n-1
        # 計算隱藏層的輸出,每個節點最終的輸出值就是權值*節點值的加權和
        for j in range(self.hidden_n[0]):  # 使用了
            total = 0.0
            for i in range(self.input_n):
                total += self.input_cells[i] * self.input_weights[i][j]
                # 此處為何是先i再j,以隱含層節點做大迴圈,輸入樣本為小迴圈,是為了每一個隱藏節點計算一個輸出值,傳輸到下一層
            self.hidden_cells[0][j] = tanh(total - self.h_b[0][j])  # 此節點的輸出是前一層所有輸入點和到該點之間的權值加權和
        for k in range(self.hidden_layers - 1):
            for m in range(self.hidden_n[k + 1]):
                total = 0.0
                for i in range(self.hidden_n[k]):
                    total += self.hidden_cells[k][i] * self.hidden_weights[k][i][m]  # 使用了一個新的東西
                self.hidden_cells[k + 1][m] = tanh(total - self.h_b[k + 1][m])  # 此節點的輸出是前一層所有輸入點和到該點之間的權值加權和
        for k in range(self.output_n):
            total = 0.0
            for j in range(self.hidden_n[-1]):
                total += self.hidden_cells[-1][j] * self.output_weights[j][k]
            self.output_cells[k] = tanh(total - self.o_b[k])  # 獲取輸出層每個元素的值
        return self.output_cells[:]  # 最後輸出層的結果返回

    # 反向傳播演算法
    def back_propagate(self, case, label, learn, correct):
        self.predict(case)  # 對例項進行預測
        output_deltas = [0.0] * self.output_n  # 初始化矩陣
        for o in range(self.output_n):
            error = label[o] - self.output_cells[o]  # 正確結果和預測結果的誤差:0,1,-1
            output_deltas[o] = tanh_derivate(self.output_cells[o]) * error  # 誤差穩定在0~1內
        # 隱含層誤差
        hidden_deltas = [None] * self.hidden_layers
        for i in range(self.hidden_layers):
            hidden_deltas[i] = [0.0] * self.hidden_n[i]

        for h in range(self.hidden_n[-1]):  # 也就是
            error = 0.0
            for o in range(self.output_n):
                error += output_deltas[o] * self.output_weights[h][o]
            hidden_deltas[-1][h] = tanh_derivate(self.hidden_cells[-1][h]) * error
            # 反向傳播演算法求W
        # 更新隱藏層->輸出權重
        for h2 in range(self.hidden_n[-1]):
            for o in range(self.output_n):
                change = output_deltas[o] * self.hidden_cells[-1][h2]  #
                # 調整權重:上一層每個節點的權重學習*變化+矯正率
                self.output_weights[h2][o] += learn * change + correct * self.output_correction[h2][o]
                self.output_correction[h2][o] = change
        # 更新隱藏1層->隱藏2權重
        for k in range(self.hidden_layers - 2, -1, -1):  # 得到了隱藏層是多少層
            for h1 in range(self.hidden_n[k]):
                for o in range(self.hidden_n[k + 1]):
                    change = hidden_deltas[k + 1][o] * self.hidden_cells[k][h1]
                    # 調整權重:上一層每個節點的權重學習*變化+矯正率
                    self.hidden_weights[k][h1][o] += learn * change + correct * self.hidden_correction[k][h1][o]
                    self.hidden_correction[k][h1][o] = change
        # 更新輸入->隱藏層的權重
        for i in range(self.input_n):  # 使用了一個東西
            for h in range(self.hidden_n[0]):
                change = hidden_deltas[0][h] * self.input_cells[i]
                self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
                self.input_correction[i][h] = change
        # 更新bias
        for o in range(self.output_n):
            self.o_b[o] = self.o_b[o] - learn * output_deltas[o]
        for k in range(self.hidden_layers):
            for h1 in range(self.hidden_n[k]):
                self.h_b[k][h1] = self.h_b[k][h1] - learn * hidden_deltas[k][h1]

        error = 0.0
        for o in range(len(label)):  # 逐漸yuce
            error = 0.5 * (label[o] - self.output_cells[o]) ** 2  # 平方誤差函式
        return error

    def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
        for i in range(limit):  # 設定迭代次數
            error = 0.0
            for j in range(len(cases)):  # 對輸入層進行訪問
                label = labels[j]
                case = cases[j]
                error += self.back_propagate(case, label, learn, correct)  # 樣例,標籤,學習率,正確閾值
            print(error)

    def test(self):  # 學習正弦函式
        cases = [
            [0, 0],
            [0, 1],
            [1, 0],
            [1, 1],
        ]
        labels = [[0], [1], [1], [0]]
        self.setup(2, [2, 2], 1)  #
        self.train(cases, labels, 10000, 0.05, 0.1)
        for case in cases:
            print(self.predict(case))


if __name__ == '__main__':
    nn = BPNeuralNetwork()
    nn.test()

相關文章