zoukankan      html  css  js  c++  java
  • 菜鸟之路——机器学习之BP神经网络个人理解及Python实现

    关键词:

    输入层(Input layer)。隐藏层(Hidden layer)。输出层(Output layer)

    理论上如果有足够多的隐藏层和足够大的训练集,神经网络可以模拟出任何方程。隐藏层多的时候就是深度学习啦

    没有明确的规则来设计最好有多少个隐藏层,可以根据实验测试的误差以及准确度来实验测试并改进。

    交叉验证方法(cross -validation):把样本分为K份,取一份为测试集,其他为训练集。共取K次,然后取其平均值

    BP的步骤

    1、初始化权重(weight)以及偏向(bias),随机初始化在[-1.1]之间或[-0.5,0.5之间,每个神经元有一个偏向]

    2、对每个特征向量进行Ij=∑wijOij。Oj=1/(1+e-Ij)。(激活函数)

                 对于输出层:Errj=Oj(1-Oj)(Tj-Oj)。Tj:真实值,Oj:预测值。

                 对于隐藏层:Errj=Oj(1-Oj)ΣErrkwjk

                 权重更新:Δwij=(l)ErrjOj。(l)是学习速率一般取[0,1]

                 偏向更新:Δθj=(l)Errj

    3、终止条件  

                 权重的更新低于某个阈值

                 预测的错误率低于某个阈值

                  达到预设的一定的循环次数

    激活函数一般为以下两个

    1,双曲函数:tanhx=(ex-e-x)/(ex+e-x)其一阶导数为1-tanh2x

    2,逻辑函数:f(x)=1/(1+e-x)   其一阶导数为f(x)(1-f(x))

    下面是代码

     1 import numpy as np
     2 
     3 def tanh(x):
     4     return np.tanh(x)
     5 
     6 def tanh_derivative(x):
     7     return 1.0-np.tanh(x)*np.tanh(x)#tanh(x)的一阶导数
     8 
     9 def logistic(x):
    10     return 1/(1+np.exp(x))
    11 
    12 def logistic_derivative(x):
    13     return logistic(x)*(1-logistic(x))
    14 
    15 
    16 class NeuralNetwork:         #面向对象编程
    17     def __init__(self,layers,activation='tanh'):#构造函数
    18         #layers,一个list,神经网络的层数以及每层神经元的个数,有几个数字就是有几层,每个数字就是每层的神经元的个数
    19         #activation:用户定义用哪个激活函数。默认为tanh
    20         if activation=='logistic':
    21             self.activation=logistic
    22             self.activation_deriv=logistic_derivative
    23         elif activation=='tanh':
    24             self.activation=tanh
    25             self.activation_deriv=tanh_derivative
    26         self.weight=[]
    27         for i in range(1,len(layers)-1):
    28             #print(layers[i - 1] + 1, layers[i] + 1)
    29             #print(layers[i] + 1, layers[i + 1] + 1)
    30             self.weight.append((2 * np.random.random((layers[i - 1] +1, layers[i] +1)) - 1) * 0.25)
    31             self.weight.append((2 * np.random.random((layers[i] +1, layers[i + 1])) - 1) * 0.25)
    32 
    33         #print("weight:", self.weight)
    34 
    35     def fit(self,X,Y,learning_rate=0.2,epochs=10000):#数据集,目标标记,学习速率,最大学习次数  #神经网络是在抽样去训练。从数据集里随便抽一个去训练。
    36         X=np.atleast_2d(X)#确认数据集至少是二维的。
    37         temp=np.ones([X.shape[0],X.shape[1]+1]) #shape返回矩阵的行数与列数。对bias偏向定义初值
    38         temp[:,0:-1]=X
    39         X=temp
    40         #print("X_:",X)
    41         Y=np.array(Y)
    42         #print("Y_:", Y)
    43         for k in range(epochs):
    44             i=np.random.randint(X.shape[0])
    45             a=[X[i]]#随机抽取一个实例
    46             #print("a_:", a)
    47             for l in range(len(self.weight)):
    48                 a.append(self.activation(np.dot(a[l],self.weight[l])))#矩阵相乘然后代入激活函数
    49                 #print("l,a[l],weight[l]:",l, a[l],self.weight[l])
    50                 #print('a__',a)
    51                 #print("error:", l, Y[i], a[-1])
    52             error =Y[i]-a[-1]
    53             deltas =[error * self.activation_deriv(a[-1])]
    54 
    55             for l in range(len(a)-2,0,-1):
    56                 deltas.append(deltas[-1].dot(self.weight[l].T)*self.activation_deriv(a[l]))  #算每一层的权重变化量
    57             deltas.reverse()#翻转一下
    58             for i in range(len(self.weight)):
    59                 layer = np.atleast_2d([a[i]])
    60                 delta = np.atleast_2d([deltas[i]])
    61                 self.weight[i]+=learning_rate*layer.T.dot(delta)  #.T就是对矩阵的转置
    62 
    63     def prdict(self,x):
    64         x=np.array(x)
    65         temp = np.ones(x.shape[0]+1)
    66         temp[0:-1]=x
    67         a=temp
    68         for l in range(0,len(self.weight)):
    69             a=self.activation(np.dot(a,self.weight[l]))
    70         return a
    71 

        这是写了一个BP神经网络的对象。

    其中有一个地方,编写的时候我一直不懂,后来研究了一下

    30 self.weight.append((2 * np.random.random((layers[i - 1] +1, layers[i] +1)) - 1) * 0.25)

    31 self.weight.append((2 * np.random.random((layers[i] +1, layers[i + 1])) - 1) * 0.25)

    37 temp=np.ones([X.shape[0],X.shape[1]+1]) #shape返回矩阵的行数与列数。对bias偏向定义初值

    38 temp[:,0:-1]=X

    39 X=temp
    这里把特征向量为什么要加一列1。经过我的研究发现加上一列1,为了防止特征值全为0,而标记却不为零的情况。因为全为0的矩阵乘以任何权向量都是0.会导致训练不成功

    然后就可以写代码进行训练了

     1 from main import NeuralNetwork#导入刚才写的对象
     2 import numpy as np
     3 
     4 nn=NeuralNetwork([2,2,1],'tanh')
     5 
     6 X = np.array([[0,0],[0,1],[1,0],[1,1]])
     7 print("X:",X)
     8 Y = np.array([1,0,0,1])    #也就是或运算嘛
     9 print("Y:",Y)
    10 nn.fit(X,Y)
    11 #print("nn:",nn)
    12 for i in [[0,0],[0,1],[1,0],[1,1]]:
    13     print(i,nn.prdict(i))

    运行结果为

    X: [[0 0]
    [0 1]
    [1 0]
    [1 1]]
    Y: [1 0 0 1]
    [0, 0] [0.99875886]
    [0, 1] [0.00025754]
    [1, 0] [1.91186633e-05]
    [1, 1] [0.99868908]

    然后又写了一个手写数字识别的程序

     1 import numpy as np
     2 from sklearn.datasets import load_digits
     3 from sklearn.metrics import confusion_matrix,classification_report #对结果的衡量
     4 from sklearn.preprocessing import LabelBinarizer                   #将[0,9]转化为如果是为1,不是就为0的样子
     5 from main import NeuralNetwork
     6 from sklearn.model_selection import train_test_split               #划分训练集与测试集
     7 
     8 digits=load_digits()
     9 X=digits.data
    10 Y=digits.target
    11 X-=X.min()
    12 X/=X.max()
    13 
    14 nn=NeuralNetwork([64,100,10])
    15 X_train,X_test,Y_train,Y_test=train_test_split(X,Y)
    16 labels_train = LabelBinarizer().fit_transform(Y_train)
    17 labels_test = LabelBinarizer().fit_transform(Y_test)
    18 print("start fitting")
    19 nn.fit(X_train,labels_train,epochs=3000)
    20 prdictions=[]
    21 for i in range(X_test.shape[0]):
    22     #print(X_test[i])
    23     o=nn.prdict(X_test[i])
    24     prdictions.append(np.argmax(o))  #最大的概率对应的那个数
    25 #print(Y_test,prdictions)
    26 print(confusion_matrix(Y_test,prdictions))
    27 print(classification_report(Y_test,prdictions))

    运行结果

    start fitting

    [[32  0  2  4  1  0  0  0  1  0]

     [ 1 17 31  2  0  0  0  0  2  0]

     [ 0  0 55  0  0  0  0  0  0  0]

     [ 0  0  8 42  0  0  0  0  0  0]

     [ 0  1  1  0 32  0  0  0  2  0]

     [ 1  7  9 21  1  1  0  0  1  0]

     [ 0  0  6  0 12  0 18  0  8  0]

     [ 1  2 36  3  1  0  0  8  0  0]

     [ 0  0 14  2  0  0  0  0 18  1]

     [ 9  2  4 11  0  0  0  1  6 12]]

                 precision    recall  f1-score   support

              0       0.73      0.80      0.76        40

              1       0.59      0.32      0.41        53

              2       0.33      1.00      0.50        55

              3       0.49      0.84      0.62        50

              4       0.68      0.89      0.77        36

              5       1.00      0.02      0.05        41

              6       1.00      0.41      0.58        44

              7       0.89      0.16      0.27        51

              8       0.47      0.51      0.49        35

              9       0.92      0.27      0.41        45

    avg / total       0.70      0.52      0.48       450

    大家注意到,第十四行,教程里面选用的“logistic”激活函数,但是如果用“logistic”得出来的结果为

    start fitting

    C:UsersadminPycharmProjectsBPmain.py:10: RuntimeWarning: overflow encountered in exp

      return 1/(1+np.exp(x))

    D:Anaconda3libsite-packagessklearnmetricsclassification.py:1135: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples.

      'precision', 'predicted', average, warn_for)

    [[51  0  0  0  0  0  0  0  0  0]

     [53  0  0  0  0  0  0  0  0  0]

     [48  0  0  0  0  0  0  0  0  0]

     [52  0  0  0  0  0  0  0  0  0]

     [35  0  0  0  0  0  0  0  0  0]

     [54  0  0  0  0  0  0  0  0  0]

     [42  0  0  0  0  0  0  0  0  0]

     [40  0  0  0  0  0  0  0  0  0]

     [34  0  0  0  0  0  0  0  0  0]

     [41  0  0  0  0  0  0  0  0  0]]

                 precision    recall  f1-score   support

              0       0.11      1.00      0.20        51

              1       0.00      0.00      0.00        53

              2       0.00      0.00      0.00        48

              3       0.00      0.00      0.00        52

              4       0.00      0.00      0.00        35

              5       0.00      0.00      0.00        54

              6       0.00      0.00      0.00        42

              7       0.00      0.00      0.00        40

              8       0.00      0.00      0.00        34

              9       0.00      0.00      0.00        41

    avg / total       0.01      0.11      0.02       450

    这个结果严重错误,上一篇我说过怎么看这个矩阵。

    我找出来这个的原因在于激活函数,如果用“logistic”,预测出来的Y全部为【0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5】然后经过np.argmax(o)就变成了0。也就解释了为什么矩阵中所有的数字都在第一列。

    但我把激活函数换成“tanhx”就没问题了。我也不知道为啥。

    看来选激活函数也是一门学问。

       到今天,监督学习的分类问题大致学完了,很浅,只是入门,接下来学回归,等把机器学习入门完了,就往深了学。

    import numpy as np
    from sklearn.datasets import load_digits
    from sklearn.metrics import confusion_matrix,classification_report #对结果的衡量
    from sklearn.preprocessing import LabelBinarizer #将[0,9]转化为如果是为1,不是就为0的样子
    from main import NeuralNetwork
    from sklearn.model_selection import train_test_split #划分训练集与测试集

    digits=load_digits()
    X=digits.data
    Y=digits.target
    X-=X.min()
    X/=X.max()

    nn=NeuralNetwork([64,100,10])
    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)
    prdictions=[]
    for i in range(X_test.shape[0]):
    #print(X_test[i])
    o=nn.prdict(X_test[i])
    prdictions.append(np.argmax(o)) #最大的概率对应的那个数
    #print(Y_test,prdictions)
    print(confusion_matrix(Y_test,prdictions))
    print(classification_report(Y_test,prdictions))
  • 相关阅读:
    Collection接口
    10linux基础-Centos7系统进程管理
    09linux基础-文档归档和压缩
    05Linux基础-vim编辑器和恢复ext4下误删除的文件
    04linux系统基础-文件的基本管理和XFS文件系统备份恢复
    03Linux基础-linux基本命令操作
    02Linux基础-linux的基础操作
    01Linux基础-环境搭建
    3、函数
    1、Python基础二
  • 原文地址:https://www.cnblogs.com/albert-yzp/p/9534891.html
Copyright © 2011-2022 走看看