当我们训练神经网络时,通常使用梯度下降法,这依赖于一个连续的、可微的实值成本函数。例如,最终的成本函数可能采用均方误差。换句话说,梯度下降法隐式地假设最终目标是回归——最小化一个实值误差度量。
有时我们希望神经网络执行分类任务——给定一个输入,将其分类到两个或多个离散类别中。在这种情况下,用户关心的最终目标是分类准确性——正确分类的案例百分比。
但是,当我们使用神经网络进行分类时,尽管我们的目标是分类准确性,但这并不是神经网络试图优化的目标。神经网络仍然在试图优化实值成本函数。有时这两个目标指向同一个方向,但有时并非如此。特别是,我遇到了一些情况,其中一个训练为正确最小化成本函数的神经网络,其分类准确性还不如一个简单的基于阈值的手动编码比较好。
我已经使用TensorFlow简化了这个问题,创建了一个最小的测试用例。它设置了一个感知器(没有隐藏层的neural网络),在绝对最小的数据集上训练它(一个输入变量,一个二进制输出变量),评估结果的分类准确性,然后将其与一个简单的基于阈值的手动编码比较的分类准确性进行比较;结果分别为60%和80%。直观上,这是因为一个具有较大输入值的单一异常值会生成相应的较大输出值,因此最小化成本函数的方法是尽力适应这个单一案例,在此过程中误分类了两个更普通的案例。感知器正确地执行了它被告知要做的任务;只是这并不符合我们对分类器的实际需求。但分类准确性不是一个连续可微函数,因此我们不能将其作为梯度下降的目标。
我们如何训练神经网络,使其最终最大化分类准确性?
import numpy as npimport tensorflow as tfsess = tf.InteractiveSession()tf.set_random_seed(1)# Parametersepochs = 10000learning_rate = 0.01# Datatrain_X = [ [0], [0], [2], [2], [9],]train_Y = [ 0, 0, 1, 1, 0,]rows = np.shape(train_X)[0]cols = np.shape(train_X)[1]# Inputs and outputsX = tf.placeholder(tf.float32)Y = tf.placeholder(tf.float32)# WeightsW = tf.Variable(tf.random_normal([cols]))b = tf.Variable(tf.random_normal([]))# Modelpred = tf.tensordot(X, W, 1) + bcost = tf.reduce_sum((pred-Y)**2/rows)optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)tf.global_variables_initializer().run()# Trainfor epoch in range(epochs): # Print update at successive doublings of time if epoch&(epoch-1) == 0 or epoch == epochs-1: print('{} {} {} {}'.format( epoch, cost.eval({X: train_X, Y: train_Y}), W.eval(), b.eval(), )) optimizer.run({X: train_X, Y: train_Y})# Classification accuracy of perceptronclassifications = [pred.eval({X: x}) > 0.5 for x in train_X]correct = sum([p == y for (p, y) in zip(classifications, train_Y)])print('{}/{} = perceptron accuracy'.format(correct, rows))# Classification accuracy of hand-coded threshold comparisonclassifications = [x[0] > 1.0 for x in train_X]correct = sum([p == y for (p, y) in zip(classifications, train_Y)])print('{}/{} = threshold accuracy'.format(correct, rows))
回答:
我们如何训练神经网络,使其最终最大化分类准确性?
我正在寻找一种方法来获得一个更接近准确性的连续代理函数
首先,目前在(深度)神经网络中用于分类任务的损失函数并不是为它们发明的,它可以追溯到几十年前,实际上来自于早期的逻辑回归。这里是二元分类的简单情况的方程:
其背后的想法正是要找到一个连续且可微的函数,以便我们能够利用(庞大且仍在扩展的)凸优化工具来解决分类问题。
可以肯定地说,鉴于上述提到的所需数学约束,目前的损失函数是我们目前拥有的最好的到目前为止的函数。
我们应该认为这个问题(即更好地逼近准确性)已经解决并结束了吗?至少在原则上,不是。我还记得一个时代,当时唯一可用的激活函数实际上是tanh
和sigmoid
;然后ReLU来了,给这个领域带来了真正的提升。同样,有人可能会最终提出一个更好的损失函数,但可以说这将会在研究论文中发生,而不是作为一个SO问题的答案…
尽管如此,当前的损失函数源自非常基本的概率和信息理论考虑(与当前的深度学习领域形成鲜明对比,这些领域建立在坚实的理论基础之上),这至少让我们对是否即将提出一个更好的损失函数提案产生了一些怀疑。
关于损失和准确性之间的关系,还有一个微妙的点,使后者在质量上与前者不同,并且在这样的讨论中经常被忽略。让我详细说明一下…
所有与此讨论相关的分类器(即神经网络、逻辑回归等)都是概率性的;也就是说,它们不返回硬类别成员(0/1),而是返回类别概率([0, 1]中的连续实数)。
为了简化讨论,仅限于二元情况,当将类别概率转换为(硬)类别成员时,我们隐式地涉及到一个阈值,通常等于0.5,例如如果p[i] > 0.5
,那么class[i] = "1"
。现在,我们可以找到许多情况下这个简单的默认阈值选择不起作用(第一个想到的是严重不平衡的数据集),我们将不得不选择一个不同的阈值。但对我们这里的讨论来说,重要的一点是,这种阈值选择虽然对准确性至关重要,但完全是外部于最小化损失的数学优化问题,并且作为它们之间的进一步“隔离层”,破坏了损失只是准确性代理的简单观点(它不是)。正如在这个Cross Validated线程的回答中很好地指出:
当你为新样本的每个类别输出一个概率时,你的练习的统计部分就结束了。选择一个阈值,超过该阈值你将新观察分类为1而不是0,不再是统计的一部分。这是决策部分的一部分。
扩大一个已经很广泛的讨论:我们是否可以完全摆脱对连续且可微函数的数学优化的(非常)限制性约束?换句话说,我们能否摆脱反向传播和梯度下降?
嗯,我们实际上已经在这样做了,至少在强化学习这个子领域中:2017年是OpenAI的新研究关于被称为进化策略的东西成为头条新闻的一年。作为额外的奖励,这里有一个超新的(2017年12月)Uber的论文关于这个主题,再次在社区中引起了极大的热情。