机器学习(十)

人工神经网络

Posted by Gavin on June 10, 2019

小楼一夜听春雨

深巷明朝卖杏花

前言

介绍

人工神经网络是深度学习中的核心,本次将学习一个ANN架构的快速教程,然后学习多层感知器(MLP),并用其解决MNIST数字分类问题。

人工神经元

有一个或多个二进制(开/关)的输入和一个二进制输出,当一定数量的输入都是激活状态时,人工神经元就会激活其输出。


感知器

介绍

感知器是最简单的ANN架构之一,它基于一个稍微不同的被称为线性阈值单元(LTU)的人工神经元:输入和输出都是数字(而不是二进制的开关状态),每个输入的连接都有一个对应的权重。LTU会加权求和所有输入,然后对求值结果应用一个阶跃函数,并产生最后的输出。

Heaviside阶跃函数:
单个LTU可以用来做简单的线性二值分类,其计算输入的线性组合,如结果超出了阈值,输出为正,否则为负。训练LTU即是找到合适的权重向量。
感知器就是单层的LTU,每个神经元都与所有输入相连,此外还会加上一个额外的偏差特征,偏差特征即是偏差神经元,永远输出1

如图是一个两个输入,三个输出的感知器,它可以将实例同时分为三个不同的二进制类,因此它也是多输出分类器。
感知器学习规则(权重更新):

如果实例是线性可分的,感知器会收敛到一个解,这是感知器收敛定理,Scikit-Learn提供了一个实现单一LTU网络的类:

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]])

PS:感知器不输出某个类概率,只能根据固定阈值来做预测,因此此类问题更应该使用逻辑回归

感知器的一些限制可以通过堆叠多个感知器来消除,这种ANN就是多层感知器(MLP)

多层感知器和反向传播

一个MLP包含一个输入层,一个或多个被称为隐藏层的LTU层,以及一个输出层。除了输出层之外,每层都包含一个偏移神经元,并与下一层完全相连。如果一个ANN有2个以及2个以上的隐藏层,则就是深度神经网络(DNN)

MLP利用反向自动微分的梯度下降算法进行训练,即对于每一个训练实例,先做一次正向预测,度量误差,然后反向遍历每个层次来度量每个连接的误差贡献度(反向过程),最后再微调每个连接的权重来降低误差。
为了让这个算法正常工作,此处将MLP的架构做了一个关键调整——把阶跃函数改成逻辑函数:\(\sigma(z)=1/(1+exp(-z))\)。这很关键,因为阶跃函数只包含平面,没有梯度(无法进行梯度下降),但逻辑函数有良好的偏导,梯度下降可以在每一步做出调整。除了逻辑函数,反向传播算法还可以和其他激活函数一起使用——双曲正切函数和ReLU函数:


MLP常常用作分类,每个输出对应一个不同的二进制分类。当每个分类是互斥的情况下,输出层通常被修改为一个共享的soft-max函数


训练MLP

TensorFlow API实现

这是一个有两个隐藏层(300、100神经元)的DNN

feature_columns = tf.contrib.learn.infer_real_valued_columns_from_input(X_train)
dnn_clf = tf.contrib.learn.DNNClassifier(hidden_units=[300,100],n_classes=10,feature_columns=feature_columns)
dnn_clf.fit(x=X_train,y=y_train.astype(np.int64),batch_size=50,steps=40000)
y_pred = list(dnn_clf.predict(X_test))
accuracy_score(y_test,y_pred)
dnn_clf.evaluate(X_test,y_test.astype(np.int64))


训练结束,进行预测!

准确率颇高!

纯实现——构建阶段

#define input and output
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, n_inputs),name='y')

def neturon_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 = tf.Variable(init,name='weights')
		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

但通常不需要自己定义连接函数,完整构造如下:

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)

with tf.name_scope('eval'):
	correct = tf.nn.in_top_k(logits, y, 1)
	accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

init = tf.global_variables_initializer()
saver = tf.train.Saver()

纯实现——执行阶段

with tf.Session() as sess:
	init.run()
	sess.run(training_op, feed_dict={X: X_train, y: y_train})
	save_path = saver.save(sess, 'model.ckpt')

纯实现——使用阶段

with tf.Session() as sess:
	saver.restore(sess, 'model.ckpt')
	Z = logits.eval(feed_dict={X: X_test})
	y_pred = tf.argmax(Z, axis=1).eval()
	accuracy_score(y_test, y_pred)