NPL实践系列:5、神经网络基础

1. 神经网络基础概念

1.1 什么是神经网络

神经网络是机器学习中的一种模型,是一种模仿动物神经网络行为特征,进行分布式并行信息处理的算法数学模型。这种网络依靠系统的复杂程度,通过调整内部大量节点之间相互连接的关系,从而达到处理信息的目的。

1.2 三类神经网络

一般来说,神经网络分三类:前馈神经网络、循环网络、对称连接网络。

  • 前馈神经网络
    这是实际应用中最常见的神经网络类型。第一层是输入,最后一层是输出。如果有多个隐藏层,我们称之为“深度”神经网络。他们计算出一系列改变样本相似性的变换。各层神经元的活动是前一层活动的非线性函数。
  • 循环网络
    循环网络在他们的连接图中定向了循环,这意味着你可以按照箭头回到你开始的地方。他们可以有复杂的动态,使其很难训练。他们更具有生物真实性。
    循环网络的目的使用来处理序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。例如,你要预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。
    循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。
  • 对称连接网络

1.3网络层数、输入层、隐藏层、输出层、隐藏单元

网络层数:数据通过几层节点的处理,就可认为是几层。
输入层:神经网络的第一层,该层接收输入信号,然后将输入信号直接向下一层进行传递,没有权重值和偏置项对输入信号进行处理。
隐藏层:该结构的主要功能是对输入数据进行不同的转换。隐藏层中的每个神经元和前一层及后一层的神经元均有连接,称这种情况为全连接的隐层。
输出层:神经网络的最后一层,该层将输出限制在一定范围,并且得到期望的输出个数。
隐藏单元:隐藏层的组成单元。

1.4激活函数。

激活函数: 此单元功能是为神经元引入非线性部分,并且将神经元的输出值压缩在一个较小的范围

2. 感知机相关;利用tensorflow等工具定义简单的几层网络(激活函数sigmoid),递归使用链式法则来实现反向传播。

  • 感知机
    感知机是1957年,由Rosenblatt提出会,是神经网络和支持向量机的基础。
    感知机是二分类的线性模型,其输入是实例的特征向量,输出的是事例的类别,分别是+1和-1,属于判别模型。
    假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练数据集正实例点和负实例点完全正确分开的分离超平面。如果是非线性可分的数据,则最后无法获得超平面。

  • tensorflow 实现反向传播

# coding:utf-8
 
import tensorflow as tf
from numpy.random import RandomState
 
# 使用命名空间定义元素,便于使用tensorboard查看神经网络图形化
with tf.name_scope('graph_1') as scope:
    batch_size = 500  # 神经网络训练集batch大小为500
    # 定义神经网络的结构,输入为2个参数,隐藏层为10个参数,输出为1个参数
    # w1为输入到隐藏层的权重,2*10的矩阵(2表示输入层有2个因子,也就是两列输入,10表示隐藏层有10个cell)
    w1 = tf.Variable(tf.random_normal([2, 10], stddev=1, seed=1), name='w1')
    # w2为隐藏层到输出的权重,10*1的矩阵(接受隐藏的10个cell输入,输出1列数据)
    w2 = tf.Variable(tf.random_normal([10, 1], stddev=1, seed=1), name='w2')
    # b1和b2均为一行,列数对应由w1和w2的列数决定
    b1 = tf.Variable(tf.random_normal([1, 10], stddev=1, seed=1), name='b1')
    b2 = tf.Variable(tf.random_normal([1, 1], stddev=1, seed=1), name='b2')
 
    # 维度中使用None,则可以不规定矩阵的行数,方便存储不同batch的大小。(占位符)
    x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input')
    y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')
 
    # 定义神经网络前向传播的过程,定义了1层隐藏层。
    # 输入到隐藏、隐藏到输出的算法均为逻辑回归,即y=wx+b的模式
    a = tf.add(tf.matmul(x, w1, name='a'), b1)
    y = tf.add(tf.matmul(tf.tanh(a), w2, name='y'), b2)  # 使用tanh激活函数使模型非线性化
    y_hat = tf.sigmoid(y)  # 将逻辑回归的输出概率化
 
    # 定义损失函数和反向传播的算法,见吴恩达视频课程第二周第三节课,逻辑回归的损失函数
    cross_entropy = - \
        tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y_hat, 1e-10, 1.0)) +
                       (1-y_)*tf.log(tf.clip_by_value((1-y_hat), 1e-10, 1.0)))
    # 方差损失函数,逻辑回归不能用
    # cost = -tf.reduce_mean(tf.square(y_ - y_hat))
    # clip_by_value函数将y限制在1e-10和1.0的范围内,防止出现log0的错误,即防止梯度消失或爆发
    
    train_step = tf.train.AdamOptimizer(0.0001).minimize((cross_entropy))  # 反向传播算法
 
    # 通过随机数生成一个模拟数据集
    rdm = RandomState(1)  # rdm为伪随机数发生器,种子为1
    dataset_size = 128000
    X = rdm.rand(dataset_size, 2)  # 生成随机数,大小为128000*2的矩阵
    # x_hat = rdm.rand(1, 2)
    x_hat = []
    x_hat.append(list(X[300]))
    print(x_hat)
 
    # 打标签,所有x1+x2<1的都被认为是正样本,其余为负样本。
    Y = [[int(x1+x2 < 1)] for (x1, x2) in X]  # 列表解析格式
    # 若x1+x2 <1为真,则int(x1+x2 <1)为1,若假,则输出为0
 
 
# 创建会话
with tf.Session() as sess:
    writer = tf.summary.FileWriter("logs/", sess.graph)
    init_op = tf.global_variables_initializer()  # 所有需要初始化的值
    sess.run(init_op)  # 初始化变量
    print(sess.run(w1))
    print(sess.run(w2))
    print('x_hat =', x_hat, 'y_hat =', sess.run(y_hat, feed_dict={x: x_hat}))
 
 
 
    STEPS = 100000
    for i in range(STEPS):
        # 每次从数据集中选batch_size个数据进行训练
        start = (i * batch_size) % dataset_size  # 训练集在数据集中的开始位置
        # 结束位置,若超过dataset_size,则设为dataset_size
        end = min(start + batch_size, dataset_size)
        
        # 通过选取的样本训练神经网络并更新参数
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
        if i % 1000 == 0:
            # 每隔一段时间计算在所有数据上的损失函数并输出
            total_cross_entropy = sess.run(
                cross_entropy, feed_dict={x: X, y_: Y})
            total_w1 = sess.run(w1)
            total_b1 = sess.run(b1)
            total_w2 = sess.run(w2)
            total_b2 = sess.run(b2)
            print("After %d training steps(s), cross entropy on all data is %g" % (
                i, total_cross_entropy))
            print('w1=', total_w1, ',b1=', total_b1)
            print('w2=', total_w2, ',b2=', total_b2)
 
    # 在训练之后神经网络权重的值
    print(sess.run(w1))
    print(sess.run(w2))
    print('x_hat =', x_hat, 'y_hat =', sess.run(y_hat, feed_dict={x: x_hat}))

3. 激活函数的种类以及各自优缺点

  • sigmoid函数
    曲线很像“S”型,也叫 Logistic 函数,用于隐层神经元输出,取值范围为(0,1)
    优点:
    它可以将一个实数映射到(0,1)的区间,可以用来做二分类。(它不像SVM直接给出一个分类的结果,Logistic Regression给出的是这个样本属于正类或者负类的可能性是多少,当然在多分类的系统中给出的是属于不同类别的可能性,进而通过可能性来分类。)
    在特征相差比较复杂或是相差不是特别大时效果比较好。
    缺点:
    激活函数计算量大,反向传播求误差梯度时,求导涉及除法
    反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练(sigmoid的饱和性)
  • Tanh函数
    也称为双切正切函数
    取值范围为[-1,1]。
    tanh在特征相差明显时的效果会很好,在循环过程中会不断扩大特征效果。
    与 sigmoid 的区别是,tanh 是 0 均值的,因此实际应用中 tanh 会比 sigmoid 更好,然而,tanh一样具有软饱和性,从而造成梯度消失。
  • ReLU函数
    输入信号 <0 时,输出都是0,>0 的情况下,输出等于输入
    ReLU 的优点:
    Krizhevsky et al. 发现使用 ReLU 得到的 SGD 的收敛速度会比 sigmoid/tanh 快很多。除此之外,当x<0时,ReLU硬饱和,而当x>0时,则不存在饱和问题。所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。这让我们能够直接以监督的方式训练深度神经网络,而无需依赖无监督的逐层预训练。
    ReLU 的缺点:
    随着训练的推进,部分输入会落入硬饱和区,导致对应权重无法更新。这种现象被称为“神经元死亡”。与sigmoid类似,ReLU的输出均值也大于0,偏移现象和 神经元死亡会共同影响网络的收敛性。
  • softmax函数
    Softmax - 用于多分类神经网络输出

4. 深度学习中的正则化(参数范数惩罚:L1正则化、L2正则化;数据集增强;噪声添加;early stop;Dropout层)、正则化的介绍。

待更新…

5. 深度模型中的优化:参数初始化策略;自适应学习率算法(梯度下降、AdaGrad、RMSProp、Adam;优化算法的选择);batch norm层(提出背景、解决什么问题、层在训练和测试阶段的计算公式);layer norm层。

待更新…

参考博客:https://www.jianshu.com/p/546dc40b52c8
https://blog.csdn.net/hzhj2007/article/details/79216955
https://www.cnblogs.com/huangyc/p/9706575.html
https://blog.csdn.net/u011584941/article/details/71534828
https://blog.csdn.net/glin_mk/article/details/81043479

你可能感兴趣的:(python,NPL,机器学习)