美文网首页
笨方法学机器学习(二)全连接神经网络

笨方法学机器学习(二)全连接神经网络

作者: 寒夏凉秋 | 来源:发表于2017-09-12 15:10 被阅读0次

网络构成

算法假设

我们的大脑接收眼睛观察传播来的数据后,会对其进行一层层的神经元去解析数据,然后得到我们对于所见的判断。然而我们对这个分析过程的了解以及脑部的研究较为浅,无法得知其脑部工作的原理。但是,我们是否可以对其进行部分抽象化为以下过程:


脑部在接收一系列数据的时候进行了一个函数式(function)的抽象,得到了其认知。

那么,我们的神经网络就是模拟这个过程,将输入信号,神经元的function处理,以及输出,都数字化。用统计与学习的方式,将这个过程一步步还原;


全连接神经网络

其全连接神经网络规则如下:

  • 神经元按照层来布局。最左边的层叫做输入层,负责接收输入数据;最右边的层叫输出层,我们可以从这层获取神经网络输出数据。输入层和输出层之间的层叫做隐藏层,因为它们对于外部来说是不可见的。
  • 同一层的神经元之间没有连接。
  • 第N层的每个神经元和第N-1层的所有神经元相连(这就是full connected的含义),第N-1层神经元的输出就是第N层神经元的输入。
  • 每个连接都有一个权值。

神经元

神经元是构成神经网络的基本单位:


一个神经元的组成为:

  • 输 入:n维度向量x
  • 线性加权:
  • 激活函数: $H(x)$,要求非线性,容易求导数
  • 输出 a

激活函数的选择

(1)sigmoid函数

(2)tanh函数

(3)relu函数

计算样例

下面我们来用最简单是sigmoid函数来尝试手算一个很简单的神经网络:


  • 中间蓝色的神经元代表了隐层神经元,其左边的是W权值,b代表了其偏置项。
  • X 为输入层
  • 红色神经元代表输出,只有一个神经元代表这个神经网络只有一个输出

x1与z1的连线权值为$0.1$,x1的输出为$0.5$,所以x1到z1的输入为$0.1$*$0.5$,同理。x2到z1的输入为$-0.06$,所以根据计算公式,z1的输入为:$0.1 0.5 +0.2-(0.3)+0.01$,再将其进行激活函数处理,得到Z1的输出:$0.50224$

神经网络的训练

  • 我们需要知道一个神经网络的每个连接上的权值.
  • 我们可以说神经网络就是一个模型,这些权值就是模型的参数(即模型要学习的东西),
  • 对于这个神经网络的连接方式,网络层数,每层的节点数,这些,我们是事先设置的,成为超参数.

目标函数与误差计算

在监督学习中,对于每一个样本,我们得到其特征$x$,标记$y$.我们通过模型$h(x)$计算得到输出值:
,显然$y$是其真实值, 是其神经网络的预测值,我们希望预测值跟真实值更接近,
数学中常用的办法是将两个值的差的平方的$1/2$来代表其接近程度:

我们将$e$当做单个成本的误差,将所有的训练样本的误差值相加,得到其误差误差项$E$:


训练的目标,就是找到合适的$w$,使误差$E$最小;

训练方法:反向传播算法(Back Propagation)

简单的说,反向传播算法的过程就是:

  • 给定的一个输入集$X$,取其中的一个向量$x$
  • 先正向计算$x$的神经网络,得到其输出值$y$
  • 训练向量$x$有人为标定的标签$l$(label),计算其预测值跟真实值之间的误差,
  • 将误差项每一层地反向传播,得到每一层的误差,然后利用每一层的误差去更新每一层的权值去拟合模型

我们先给出各层的计算公式,然后再进行数学公式的推导(不擅长数学的可以跳过,等代码实现再回来看推导)

(1) 输出层的误差计算:



其中, 是节点i的误差值,$Yi$是节点$i$的输出值,$Ti$是对应节点$i$的目标值

(2) 隐藏层的误差计算与传递



我们知道:

$ai$是节点i的输出值,$Wki$节点i到它下一层节点$i$连接的权重, 是节点$i$到下一层节点$k$的误差项

更新权重:


数学公式的推导:

我们取网络所有输出层节点的误差平方和作为目标函数:


,
用随机梯度下降算法对目标函数进行优化:



我们可以从上图得到,$W84$仅影响$a4$节点到$8$节点的输入值,设:$netj$为节点$j$的加权输入:


$Ed$是$netj$的函数,而$netj$是$Wji$的函数,根据链式求导法则:



$xji$是节点i到节点j的输出值,即$j$的输入值

输出层:

netj仅影响节点j的输出值,,所以Ed是yi的函数,yi是netj的函数:



计算第一项
将Ed带入公式,得到:


计算第二项:


综合,得:


将其推导代入 随机推导公式,得:


隐层:

我们定义节点j的所有直接下游节点的集合为:$Downstream(j)$,所以netj只能通过影响$Downstream(j)$来影响$Ed$,设Netk是节点J的下游输入,而$Ed$是netk的函数,而netk是netj的函数,所以我们用全导数公式:



因为: ,代入,得:

自此,我们完成了公式的推导

代码:

import numpy as np

#定义tan函数以及tan函数的导数
def tanh(x):
    return np.tanh(x)

def tanh_deriv(x):
    return  1.0-np.tanh(x)*np.tanh(x)

#定义logistich函数以及其导数
def logistic(x):
    return 1/(1+np.exp(-x))

def logistic_derivatrive(x):
    return logistic(x)*(1-logistic(x))

class NeturalNetwork(object):
    def __init__(self,layers,activations='tanh'):
        '''
        :param layers: 一个list  包括每一层的神经元数
        :param activations:激活函数
        '''
        if activations=='tanh':
            self.activation = tanh
            self.activation_deriv = tanh_deriv
        if activations=='logistic':
            self.activation = logistic
            self.activation_deriv = logistic_derivatrive
        self.weights = []
        for i in range(1,len(layers)-1):
            #i跟前一层的权重
            self.weights.append((2*np.random.random((layers[i-1]+1,layers[i]+1))-1)*0.25)
            #i层跟i+1层进行赋值 权重
            self.weights.append((2*np.random.random((layers[i]+1,layers[i+1]))-1)*0.25)

    def fit(self,X,y,learning_rate = 0.2,epochs=10000):
        '''
        :param X:
        :param y:
        :param learning_rate: 学习率
        :param epochs: 学习步骤
        :return:
        '''
        #二维矩阵
        X = np.atleast_2d(X)
        #ones 矩阵全是1   shape函数返回的是行列数(返回一个List)跟X一样维度的矩阵
        temp = np.ones([X.shape[0],X.shape[1]+1])
        #temp等于第一列到最后一列跟x一样的矩阵
        temp  [:,0:-1]=X
        X= temp
        Y=np.array(y)

        #第几次循环
        for k in range(epochs):
            i = np.random.randint(X.shape[0])
            #随机取一个数,代表第i行,对i行数据进行更新
            a = [X[i]]
            #形成这一行数据为输入的神经网络,dot代表内积
            for l in range(len(self.weights)):
                a.append(self.activation(np.dot(a[l],self.weights[l])))
            #误差
            error = y[i]-a[-1]
            deltas = [error * self.activation_deriv(a[-1])]
            #开始往回算每一层的误差
            #deltas是所有权重的误差列表
            for l in range(len(a)-2,0,-1):
                deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_deriv(a[l]))
            deltas.reverse()
            for i in range(len(self.weights)):
                layers = np.atleast_2d(a[i])
                delta = np.atleast_2d(deltas[i])
                self.weights[i] += learning_rate*layers.T.dot(delta)
    def predict(self,x):
        x = np.array(x)
        temp = np.ones(x.shape[0]+1)
        temp[0:-1] = x
        a =temp
        for l in range(0,len(self.weights)):
            a = self.activation(np.dot(a,self.weights[l]))
        return a
用手写训练集来测试我们的神经网络:
import numpy as np
from sklearn.datasets import load_digits
from sklearn.metrics import confusion_matrix,classification_report
from sklearn.preprocessing import LabelBinarizer
from neuralNetwork.nn import NeturalNetwork
from sklearn.cross_validation import train_test_split

digits =load_digits()
#1797张 8*8的手写数字图片
X = digits.data
Y = digits.target
#标准化
X -=X.min()
X /=X.max()

nn = NeturalNetwork([64,100,10],'logistic')

#分离测试集跟训练集
X_train,X_test,y_train,y_test = train_test_split(X,Y)
labels_train = LabelBinarizer().fit_transform(y_train)
labels_test  = LabelBinarizer().fit_transform(y_test)

print("start fitting")

nn.fit(X_train,labels_train,epochs=3000)

print("end training")
predictions=[]

for i in range(X_test.shape[0]):
    o = nn.predict(X_test[i])
    predictions.append(np.argmax(o))

#10*10矩阵(分类是10) ,对角线表示预测的对,行是预测值,列是真实值
print(confusion_matrix(y_test,predictions))
'''
v[[43  0  0  0  0  0  0  0  0  0]
 [ 0 37  0  0  0  0  1  0  0  8]
 [ 0  1 38  3  0  0  0  0  0  0]
 [ 0  0  1 47  0  1  0  1  0  0]
 [ 0  0  0  0 47  0  0  0  0  1]
 [ 0  0  0  0  0 48  1  0  0  0]
 [ 0  2  0  0  0  0 38  0  0  0]
 [ 0  0  0  0  1  0  0 37  0  1]
 [ 1  7  0  1  0  4  1  0 26  6]
 [ 0  0  0  4  0  0  0  0  0 43]]
'''
print(classification_report(y_test,predictions))
'''统计
             precision    recall  f1-score   support

          0       0.98      1.00      0.99        43
          1       0.79      0.80      0.80        46
          2       0.97      0.90      0.94        42
          3       0.85      0.94      0.90        50
          4       0.98      0.98      0.98        48
          5       0.91      0.98      0.94        49
          6       0.93      0.95      0.94        40
          7       0.97      0.95      0.96        39
          8       1.00      0.57      0.72        46
          9       0.73      0.91      0.81        47

avg / total       0.91      0.90      0.90       450

'''

用向量式编程的思想改进我们的代码:

# -*- coding:utf-8 -*-
#!/usr/bin/local/bin/python

import numpy as np

class FullConnectedLayer(object):
    def __init__(self,input_size,
                 output_size,
                 learing_rate,
                 activator):
        self.input_size = input_size
        self.output_size = output_size
        self.activator = activator
        self.learning_rate = learing_rate
        self.W = np.random.uniform(-0.1,0.1,(output_size,input_size))
        self.b = np.zeros((output_size,1))
        self.output = np.zeros((output_size,1))
    def forward(self,input_array):
        self.input = input_array
        self.output = self.activator.forward(
            np.dot(self.W,input_array)+self.b
        )

    def backward(self,delta_array):
        self.delta = self.activator.backward(self.input) * np.dot(
            self.W.T,delta_array
        )
        self.W_grad = np.dot(delta_array,self.input.T)
        self.b_grad = delta_array

    def update(self):
        self.W += self.learning_rate * self.W_grad
        self.b += self.learning_rate * self.b_grad

相关文章

网友评论

      本文标题:笨方法学机器学习(二)全连接神经网络

      本文链接:https://www.haomeiwen.com/subject/zkmcsxtx.html