本章讨论一种全新的建模理念,它不关心模型的假设以及相应的数学推导,也就是说不关心模型的可解释性,其核心内容是模型实现,虽然到目前为止,人们还无法理解,但在某些特定场景里预测效果却非常好。我们把这种模型称为神经网络,深度学习,人工智能等。
神经元
模型的输入是数据里的自变量
接收输入变量的是一个线性模型
接下来是一个非线性激活函数,这是神经元模型的核心,常用的激活函数有很多,如sigmoid,ReLU,tanh,maxout等
模型各部分联结起来得到输出,传递给下一个神经元
sigmoid 神经元与二元逻辑回归
sigmoid 函数作为神经元激活函数的两个好处:
实用:能将任意实数映射到(0,1)区间
理论:模拟了两种效应的竞争关系,可近似表达正效应大于负效应的概率
当模型被用于二元分类时,sigmoid神经元模型就是逻辑回归
softmax 函数与多元逻辑回归
独热编码:对于k元分类问题,用一个k维行向量来表示它的类别
Softmax 函数:给定输入数据,输出这个数据的类别概率
(softmax 函数)
对于k元逻辑回归,某个数据点的损失可以写成softmax函数与行向量的乘积形式
其中是独热编码向量,,是Softmax函数
神经网络
神经元按层组织,每一层包含若干神经元,层内神经元相互独立,但相邻两层之间是全连接的
神经网络不同层按功能分为3类,分别是输入层,隐藏层,和输出层。
隐藏层里每一个圆圈包含线性模型和激活函数,统称神经元
输入层不对数据做处理
输出层只包含线性模型,没有激活函数
代码实现
借助第三方库tensorflow实现
class ANN(object):
def __init__(self, size, logPath):
"""
创建一个神经网络
"""
# 重置tensorflow的graph,确保神经网络可多次运行
tf.reset_default_graph()
tf.set_random_seed(1908)
self.logPath = logPath
self.layerNum = len(size)
self.size = size
def defineANN(self):
"""
定义神经网络的结构
"""
# self.input是训练数据里自变量
prevSize = self.input.shape[1].value
prevOut = self.input
# self.size是神经网络的结构,也就是每一层的神经元个数
size = self.size
layer = 1
# 定义隐藏层
for currentSize in size[:-1]:
weights = tf.Variable(
tf.truncated_normal([prevSize, currentSize],
stddev=1.0 / np.sqrt(float(prevSize))))
# 记录隐藏层的模型参数
tf.summary.histogram("hidden%s" % layer, weights)
layer += 1
biases = tf.Variable(tf.zeros([currentSize]))
prevOut = tf.nn.sigmoid(tf.matmul(prevOut, weights) + biases)
prevSize = currentSize
# 定义输出层
weights = tf.Variable(
tf.truncated_normal([prevSize, size[-1]],
stddev=1.0 / np.sqrt(float(prevSize))))
biases = tf.Variable(tf.zeros([size[-1]]))
self.out = tf.matmul(prevOut, weights) + biases
return self
def defineLoss(self):
"""
定义神经网络的损失函数
"""
# 定义单点损失,self.label是训练数据里的标签变量
loss = tf.nn.softmax_cross_entropy_with_logits(
labels=self.label, logits=self.out, name="loss")
# 定义整体损失
self.loss = tf.reduce_mean(loss, name="average_loss")
return self
def SGD(self, X, Y, learningRate, miniBatchFraction, epoch):
"""
使用随机梯度下降法训练模型
参数
----
X : np.array, 自变量
Y : np.array, 因变量
"""
# 记录训练的细节
tf.summary.scalar("loss", self.loss)
summary = tf.summary.merge_all()
method = tf.train.GradientDescentOptimizer(learningRate)
optimizer= method.minimize(self.loss)
batchSize = int(X.shape[0] * miniBatchFraction)
batchNum = int(np.ceil(1 / miniBatchFraction))
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
summary_writer = tf.summary.FileWriter(self.logPath, graph=tf.get_default_graph())
step = 0
while (step < epoch):
for i in range(batchNum):
batchX = X[i * batchSize: (i + 1) * batchSize]
batchY = Y[i * batchSize: (i + 1) * batchSize]
sess.run([optimizer],
feed_dict={self.input: batchX, self.label: batchY})
step += 1
# 将日志写入文件
summary_str = sess.run(summary, feed_dict={self.input: X, self.label: Y})
summary_writer.add_summary(summary_str, step)
summary_writer.flush()
self.sess = sess
return self
def fit(self, X, Y, learningRate=0.3, miniBatchFraction=0.1, epoch=2500):
"""
训练模型
参数
----
X : np.array, 自变量
Y : np.array, 因变量
"""
self.input = tf.placeholder(tf.float32, shape=[None, X.shape[1]], name="X")
self.label = tf.placeholder(tf.int64, shape=[None, self.size[-1]], name="Y")
self.defineANN()
self.defineLoss()
self.SGD(X, Y, learningRate, miniBatchFraction, epoch)
def predict_proba(self, X):
"""
使用神经网络对未知数据进行预测
"""
sess = self.sess
pred = tf.nn.softmax(logits=self.out, name="pred")
prob = sess.run(pred, feed_dict={self.input: X})
return prob
反向传播算法(BP)
反向传播算法是神经网络的基石,但其本身却饱受质疑,因为没有生物学的支持,违背神经网络的建模理念,BP算法的核心是利用链式法则计算多元函数偏导数
计算的起点:
反向传播:
模型训练梯度:
BP算法和最大期望EM算法类似,可分为向前计算和向后计算
- 首先随机生成模型参数和
- 根据现有的模型参数和训练数据,计算每个神经元的和
- 根据随机梯度下降法更新参数
提高神经网络的学习效率
改进激活函数
学习效率正比于激活函数的导数
sigmoid函数:非0中心;自变量远离0时导数接近0
tanh函数:以0为中心但是自变量远离0时导数接近0
ReLU函数:自变量小于0时,导数恒小于0,自变量大于0时,导数恒大于0
参数初始化:令,其中m是上一层神经元个数,可以提高学习效率
梯度消失和梯度爆炸是同一个问题的两个极端,是神经网络的权重项叠加效应导致。