1. 程式人生 > >全連接神經網絡 MLP

全連接神經網絡 MLP

pri num and height eight 變量 方差 calc range

全連接神經網絡 MLP

最近開始進行模型壓縮相關課題,復習一下有關的基礎知識。

1. MLP簡介

技術分享圖片

上圖是一個簡單的MLP,這是典型的三層神經網絡的基本構成,Layer L1是輸入層,Layer L2是隱含層,Layer L3是隱含層。

為了方便下面的公式描述,引入一張帶公式的圖。

技術分享圖片

i是input層,h是hide層,o是output層。

2. MLP 正向傳播

正向傳播其實就是預測過程,就是由輸入到輸出的過程。

技術分享圖片

為之前的圖片賦上初值,

技術分享圖片

技術分享圖片

上述變量中,存在著如下過程:原始輸入-> 帶權計算-> net_h1-> 激活函數-> out_h1

同理,可以計算另一個隱層net_h2, out_h2,以及輸出層net_o1, net_o2, out_o1, out_o2

此時在輸出端我們可以得到一個預測值,但是在隨機初始化權值的情況下,這個值一定還有上升的空間,怎麽才能使這個值變得更為準確呢?

3. MLP 反向傳播

MLP的反向傳播過就是對於神經網絡的訓練過程。在這裏,我們訓練的是之前各條邊上的權值。

3.1 總誤差 (square error)

技術分享圖片

target為該樣本的正確值,output為這一輪預測的值。

技術分享圖片

這裏存在兩個輸出,所以,對於所有輸出求和,並最終計算E_total

技術分享圖片

推廣至N個輸出(分類),則是把N各分類中的輸出(一般是分類概率)誤差分別求出,最終求和。

在這裏的總誤差在下面的應用時,主要看的是接受到了幾個誤差的影響(如果只接受到一個誤差的影響,那就只使用一個誤差)。

3.2 輸出層參數更新

以權重參數w5為例,如果我們想知道w5對整體誤差產生了多少影響,可以用整體誤差對w5求偏導求出:(鏈式求導法則)

技術分享圖片

技術分享圖片

現在我們來分別計算每個式子的值:

計算技術分享圖片

技術分享圖片

計算技術分享圖片

技術分享圖片

(這一步實際上就是對sigmoid函數求導,比較簡單,可以自己推導一下)

計算技術分享圖片

技術分享圖片

最後三者相乘:

技術分享圖片

這樣我們就計算出整體誤差E(total)對w5的偏導值。

回過頭來再看看上面的公式,我們發現:

技術分享圖片

為了表達方便,用技術分享圖片來表示輸出層的誤差:

技術分享圖片

因此,整體誤差E(total)對w5的偏導公式可以寫成:

技術分享圖片

如果輸出層誤差計為負的話,也可以寫成:

技術分享圖片

最後我們來更新w5的值:

技術分享圖片

(其中,技術分享圖片是learning rate,這裏我們取0.5)

3.3 隱含層參數更新

技術分享圖片

計算技術分享圖片

技術分享圖片

先計算技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

同理,計算出:

          技術分享圖片

兩者相加得到總值:

技術分享圖片

再計算技術分享圖片

技術分享圖片

再計算技術分享圖片

技術分享圖片

最後,三者相乘:

技術分享圖片

為了簡化公式,用sigma(h1)表示隱含層單元h1的誤差:

技術分享圖片

最後,更新w1的權值:

技術分享圖片

Python代碼:

#coding:utf-8
import random
import math

#
#   參數解釋:
#   "pd_" :偏導的前綴
#   "d_" :導數的前綴
#   "w_ho" :隱含層到輸出層的權重系數索引
#   "w_ih" :輸入層到隱含層的權重系數的索引

class NeuralNetwork:
    LEARNING_RATE = 0.5

    def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
        self.num_inputs = num_inputs

        self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
        self.output_layer = NeuronLayer(num_outputs, output_layer_bias)

        self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
        self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)

    def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):
        weight_num = 0
        for h in range(len(self.hidden_layer.neurons)):
            for i in range(self.num_inputs):
                if not hidden_layer_weights:
                    self.hidden_layer.neurons[h].weights.append(random.random())
                else:
                    self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])
                weight_num += 1

    def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
        weight_num = 0
        for o in range(len(self.output_layer.neurons)):
            for h in range(len(self.hidden_layer.neurons)):
                if not output_layer_weights:
                    self.output_layer.neurons[o].weights.append(random.random())
                else:
                    self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
                weight_num += 1

    def inspect(self):
        print(‘------‘)
        print(‘* Inputs: {}‘.format(self.num_inputs))
        print(‘------‘)
        print(‘Hidden Layer‘)
        self.hidden_layer.inspect()
        print(‘------‘)
        print(‘* Output Layer‘)
        self.output_layer.inspect()
        print(‘------‘)

    def feed_forward(self, inputs):
        hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
        return self.output_layer.feed_forward(hidden_layer_outputs)

    def train(self, training_inputs, training_outputs):
        self.feed_forward(training_inputs)

        # 1. 輸出神經元的值
        pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
        for o in range(len(self.output_layer.neurons)):

            # ?E/?z?
            pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])

        # 2. 隱含層神經元的值
        pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):

            # dE/dy? = Σ ?E/?z? * ?z/?y? = Σ ?E/?z? * w??
            d_error_wrt_hidden_neuron_output = 0
            for o in range(len(self.output_layer.neurons)):
                d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]

            # ?E/?z? = dE/dy? * ?z?/?
            pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()

        # 3. 更新輸出層權重系數
        for o in range(len(self.output_layer.neurons)):
            for w_ho in range(len(self.output_layer.neurons[o].weights)):

                # ?E?/?w?? = ?E/?z? * ?z?/?w??
                pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)

                # Δw = α * ?E?/?w?
                self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight

        # 4. 更新隱含層的權重系數
        for h in range(len(self.hidden_layer.neurons)):
            for w_ih in range(len(self.hidden_layer.neurons[h].weights)):

                # ?E?/?w? = ?E/?z? * ?z?/?w?
                pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)

                # Δw = α * ?E?/?w?
                self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight

    def calculate_total_error(self, training_sets):
        total_error = 0
        for t in range(len(training_sets)):
            training_inputs, training_outputs = training_sets[t]
            self.feed_forward(training_inputs)
            for o in range(len(training_outputs)):
                total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
        return total_error

class NeuronLayer:
    def __init__(self, num_neurons, bias):

        # 同一層的神經元共享一個截距項b
        self.bias = bias if bias else random.random()

        self.neurons = []
        for i in range(num_neurons):
            self.neurons.append(Neuron(self.bias))

    def inspect(self):
        print(‘Neurons:‘, len(self.neurons))
        for n in range(len(self.neurons)):
            print(‘ Neuron‘, n)
            for w in range(len(self.neurons[n].weights)):
                print(‘  Weight:‘, self.neurons[n].weights[w])
            print(‘  Bias:‘, self.bias)

    def feed_forward(self, inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.calculate_output(inputs))
        return outputs

    def get_outputs(self):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.output)
        return outputs

class Neuron:
    def __init__(self, bias):
        self.bias = bias
        self.weights = []

    def calculate_output(self, inputs):
        self.inputs = inputs
        self.output = self.squash(self.calculate_total_net_input())
        return self.output

    def calculate_total_net_input(self):
        total = 0
        for i in range(len(self.inputs)):
            total += self.inputs[i] * self.weights[i]
        return total + self.bias

    # 激活函數sigmoid
    def squash(self, total_net_input):
        return 1 / (1 + math.exp(-total_net_input))


    def calculate_pd_error_wrt_total_net_input(self, target_output):
        return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();

    # 每一個神經元的誤差是由平方差公式計算的
    def calculate_error(self, target_output):
        return 0.5 * (target_output - self.output) ** 2

    
    def calculate_pd_error_wrt_output(self, target_output):
        return -(target_output - self.output)

    
    def calculate_pd_total_net_input_wrt_input(self):
        return self.output * (1 - self.output)


    def calculate_pd_total_net_input_wrt_weight(self, index):
        return self.inputs[index]


# 文中的例子:

nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
for i in range(10000):
    nn.train([0.05, 0.1], [0.01, 0.09])
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))


#另外一個例子,可以把上面的例子註釋掉再運行一下:

# training_sets = [
#     [[0, 0], [0]],
#     [[0, 1], [1]],
#     [[1, 0], [1]],
#     [[1, 1], [0]]
# ]

# nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
# for i in range(10000):
#     training_inputs, training_outputs = random.choice(training_sets)
#     nn.train(training_inputs, training_outputs)
#     print(i, nn.calculate_total_error(training_sets))

全連接神經網絡 MLP