阅读书籍为《Hands-On Machine Learning with Scikit-Learn & TensorFlow》王静源等翻译的中文译版《机器学习实战,基于 Scikit-Learn 和 TensorFlow》,本文中所有图片均来自于书籍相关部分截图。
人工神经网络三两事
-
为什么叫ANN:
人类看着飞鸟造出了飞机,看着蝙蝠造出了雷达,学着海豚造出了声纳,然后学着自己的神经元及其网状结构开发出了ANN(Artificial Neural Networks)。
-
ANN的发展:
-
20世纪四十年代,1943年由神经学家Warren McCulloch和数学家Walter Pitts首次提出。两人通过简化的的计算模型来描述动物大脑中神经元如何通过命题逻辑来实现复杂计算。
-
20世纪六十年代,ANN陷入黑暗。
-
20世纪八十年代,随着新网络架构的发明以及更好的培训技术,ANN重新被关注。
-
20世纪九十年代,SVM的流行超过ANN。
-
九十年代之后进入新世纪以来,飞速增长的计算能力和数以百万计的强大GPU使得ANN可以在能够容忍的时间内训练大型神经网络。自此ANN进入了新一轮高潮。
一个简单的神经元
-
一个生物神经元
生物神经元小A通过突触接受从其他细胞发来的电脉冲(信号),当在一定的时间内收到足够多的信号,小A就会发出它自己的信号。无数个这样的小A连接成层,连接成网组成了我们的神经网络,帮我们接受信息,处理信息,分析信息,最终告诉我们的身体该怎样反应。 -
1943年那个简化的神经元计算模型
C=A: 等同计算单元,如果A是(非)激活的那么C也是(非)激活的。
C=A^B: 逻辑与计算单元,当A和B都处于激活状态,C才能激活。
C=AvB: 逻辑或计算单元,当A和B中至少有一个是激活状态时,C才可以激活。
C=A^!B: 逻辑异或计算单元,当A激活且B非激活状态时,C才可以激活。
一个简单的ANN
最简单的ANN架构之一就是1957年Frank Rosenblatt发明的感知器。
-
感知器是单层的LTU
每个神经元都与所有输入相连,除此外还要加上一个额外的偏差特征。这就意味着感知器中的每一个LTU都要计算所有输入以及一个偏差特征。
LTU: 线性阈值单元,输入一组数字,对其中每个数字分别加权然后求和,再对求和结果进行阶跃函数求值最后输出。单个的LTU可以用来做简单的线性二值分类。训练LTU的意思是找出最佳权重组。
感知器中常见的阶跃函数:
-
感知器的训练Hebb定律
如果一个神经元总是处罚另外的神经元,那么这两个神经元之间的连接就会变的更强。**当两个神经元有相同的输出时,他们之间的链接权重就会增强。**更新规则如下:
Wi,j是第i个输入的神经元和第j个输入的神经元之间的连接权重。
Xi是当前训练实例的第i输入值。
**(Yj)^**是当前训练实例的第j输出神经元的输出值。
Yj是当前训练实例的第j输出神经元的目标输出。
η是学习速率。 -
感知器分类鸢尾花
import numpy as np from sklearn.datasets import load_iris from sklearn.linear_model import Perceptron iris = load_iris() X = iris.data[:, (2, 3)] Y = (iris.target == 0).astype(np.int) per_clf = Perceptron(random_state=42) per_clf.fit(X, Y) y_pred = per_clf.predict([[2, 0.5]]) print(y_pred)
感知器不会输出某个类的概率,它只能根据阈值做出预测。 感知器也无法处理异或分类问题。不过异或的分类问题可以通过架构多层感知器(MLP)而解决。
可以通过感知器的叠加解决异或问题,如下图所示,当输入(1,1)(0,0)是结果为0,当(1, 0)或者(0,1)时,这个两层的感知器就可以完成异或的功能。
-
多层感知器MLP
1.一个多层感知器包含一个输入层,一个或多个隐藏层(感知器)和一个被称为输出层的最终的感知器层。
2.去输入层之外,每层都会有一个偏移神经元与下一层全连接。
3.如果一个ANN的层数大于等于两层,则被称为深度神经网络DNN。
-
多层感知器的训练
1986年,D.E.Rumelhart发表了一篇介绍反向传播训练算法的开创性论文,今天我们称其为反向自动微分的梯度下降法。
1.对于每一个训练实例,算法将其发送到网络中并计算每个连续层中的每个神经元的输出(正向过程,常规操作)。
2.算法度量网络的输出误差,对比期望值和实际网络输出(开始回头)。
3.算法计算最后一个隐藏层中每个神经元对输出神经元的误差的贡献度(反向第一步)。
4.继续测量这些误差贡献中有多少来自前一个隐藏层中的每个神经元。这个过程一直回推到输入层(反向的剩下几步)。
这个反向传递的过程通过网络中向后传播误差梯度有效的测量网络中所有连接权重的误差梯度,最终通过微调每个连接的权重来降低误差(梯度下降)。 -
MLP通常被用来分类,当每个类别为互斥的时候,输出层通常会被修改成一个共享的soft_max函数。其中每个神经元的输出对应与相应分类的估计概率。这种网络是前馈神经网络(FNN)的一个范例。
用TF实现一下吧
from tensorflow.contrib.layers import fully_connected
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import numpy as np
#导入数据集
mnist = input_data.read_data_sets("/tmp/data/")
n_epochs = 400
batch_size = 50
#定义输入规格
n_inputs = 28*28
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10
#定义数据占位符
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
Y = tf.placeholder(tf.int64, shape=(None), name="X")
#创建神经网络,占位符结点X用作输入层数据,执行阶段每次都被训练数据替换;
#神经元数量,层次名,激活函数
def neuron_layer(X, n_neurons, name, activation=None):
#通过层的名称创建一个作用域,会包含这一层的所有结点
with tf.name_scope(name):
#获取输入矩阵的第二个维度
n_inputs = int(X.get_shape()[1])
#用输入矩阵的第二个维度的尺寸决定输入的数量
stddev = 2 / np.sqrt(n_inputs)
init = tf.truncated_normal((n_inputs, n_neurons), stddev=stddev)
#创建一个保存权重矩阵的变量W,这是一张二维表,包含每个输入和神经元之间的连接权重
w = tf.Variable(init, name="weights")
#创建偏差,初始化为0
b = tf.Variable(tf.zeros([n_neurons]), name="biases")
#创建子图,对加权与偏差求和
z = tf.matmul(X, w) + b
#判断是否设置激活函数
if activation == "relu":
return tf.nn.relu(z)
else:
return z
#连接相关层1
with tf.name_scope("dnn"):
hidden1 = neuron_layer(X, n_hidden1, "hidden1", activation="relu")
hidden2 = neuron_layer(hidden1, n_hidden2, "hidden2", activation="relu")
logits = neuron_layer(hidden2, n_outputs, "outputs")
#连接相关层 2 (可以直接用全连接函数,如果没有定义自己的神经层函数)
with tf.name_scope("dnn"):
hidden1 = fully_connected(X, n_hidden1, scope="hidden1")
hidden2 = fully_connected(hidden1, n_hidden2, scope="hidden2")
logits = fully_connected(hidden2, n_outputs, scope="outputs", activation_fn=None)
#使用函数测量平均交叉熵
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=Y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
learning_rate = 0.01
#定义梯度下降优化器找最佳训练参数
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
#指定如何对模型求值,我们简单的将精度用作性能指标
#对于每个实例通过检测最高的Logit值是否对应目标类来判断预测是否正确
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, Y, 1)
accuracy = tf.reduce_mean(tf.case(correct, tf.float32))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
#创建会话执行网络最后保存
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
for iter in range(mnist.train.num_examples // batch_size):
X_batch, Y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={X: X_batch, Y: Y_batch})
acc_train = accuracy.eval(feed_dict={X: X_batch, Y: Y_batch})
acc_test = accuracy.eval(feed_dict={X: mnist.test.images,
Y: mnist.test.labels})
print(epoch, "train accuracy:", acc_train, "test acc : ", acc_test)
save_path = saver.save(sess, "./model_final.ckpt")
超级灵活的网络怎么调参
神经网络超多的超参数为其提供了的巨大的灵活性,但同时也为模型的训练及参数调整带来了巨大的不变,在此有一下几种思路帮助调参: