<blockquote>
<p>How can we train a neural network so that it ends up maximizing classification accuracy?</p>
<p>I'm asking for a way to get a continuous proxy function that's closer to the accuracy</p>
</blockquote>
<p>To start with, the loss function used today for classification tasks in (deep) neural nets was not invented with them, but it goes back several decades, and it actually comes from the early days of logistic regression. Here is the equation for the simple case of binary classification:</p>
<p><a href="https://i.stack.imgur.com/ganiv.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ganiv.png" alt="enter image description here" /></a></p>
<p>The idea behind it was exactly to come up with a <em>continuous &amp; differentiable</em> function, so that we would be able to exploit the (vast, and still expanding) arsenal of convex optimization for classification problems.</p>
<p>It is safe to say that the above loss function is the best we have <em>so far</em>, given the desired mathematical constraints mentioned above.</p>
<p>Should we consider this problem (i.e. better approximating the accuracy) solved and finished? At least in principle, no. I am old enough to remember an era when the only activation functions practically available were <code>tanh</code> and <code>sigmoid</code>; then came ReLU and gave a real boost to the field. Similarly, someone may eventually come up with a better loss function, but arguably this is going to happen in a research paper, and not as an answer to a SO question...</p>
<p>That said, the very fact that the current loss function comes from very <em>elementary</em> considerations of probability and information theory (fields that, in sharp contrast with the current field of deep learning, stand upon firm theoretical foundations) creates at least some doubt as to if a better proposal for the loss may be just around the corner.</p>
<hr />
<p>There is another subtle point on the relation between loss and accuracy, which makes the latter something qualitatively different than the former, and is frequently lost in such discussions. Let me elaborate a little...</p>
<p>All the classifiers related to this discussion (i.e. neural nets, logistic regression etc) are <em>probabilistic</em> ones; that is, they do not return hard class memberships (0/1) but class probabilities (continuous real numbers in [0, 1]).</p>
<p>Limiting the discussion for simplicity to the binary case, when converting a class probability to a (hard) class membership, we are implicitly involving a <em>threshold</em>, usually equal to 0.5, such as if <code>p[i] &gt; 0.5</code>, then <code>class[i] = &quot;1&quot;</code>. Now, we can find many cases whet this naive default choice of threshold will not work (heavily imbalanced datasets are the first to come to mind), and we'll have to choose a different one. But the important point for our discussion here is that this threshold selection, while being of central importance to the accuracy, is completely <em>external</em> to the mathematical optimization problem of minimizing the loss, and serves as a further &quot;insulation layer&quot; between them, compromising the simplistic view that loss is just a proxy for accuracy (it is not). As nicely put in the answer of <a href="https://stats.stackexchange.com/questions/312119/reduce-classification-probability-threshold">this Cross Validated thread</a>:</p>
<blockquote>
<p>the statistical component of your exercise ends when you output a probability for each class of your new sample. Choosing a threshold beyond which you classify a new observation as 1 vs. 0 is not part of the <em>statistics</em> any more. It is part of the <em>decision</em> component.</p>
</blockquote>
<hr />
<p>Enlarging somewhat an already broad discussion: Can we possibly move completely away from the (very) limiting constraint of mathematical optimization of continuous &amp; differentiable functions? In other words, can we do away with back-propagation and gradient descend?</p>
<p>Well, we are actually doing so already, at least in the sub-field of reinforcement learning: 2017 was the year when <a href="https://blog.openai.com/evolution-strategies/" rel="nofollow noreferrer">new research from OpenAI</a> on something called <em>Evolution Strategies</em> <a href="https://www.technologyreview.com/s/603921/elon-musks-openai-unveils-a-simpler-way-for-machines-to-learn/" rel="nofollow noreferrer">made headlines</a>. And as an extra bonus, here is an ultra-fresh (Dec 2017) <a href="https://arxiv.org/abs/1712.06567" rel="nofollow noreferrer">paper by Uber</a> on the subject, again generating <a href="https://twitter.com/dennybritz/status/943384648595357696" rel="nofollow noreferrer">much enthusiasm</a> in the community.</p>
Cost function training target versus accuracy desired goal
<p>When we train neural networks, we typically use gradient descent, which relies on a continuous, differentiable real-valued cost function. The final cost function might, for example, take the mean squared error. Or put another way, gradient descent implicitly assumes the end goal is <em>regression</em> - to minimize a real-valued error measure.</p>

<p>Sometimes what we want a neural network to do is perform <em>classification</em> - given an input, classify it into two or more discrete categories. In this case, the end goal the user cares about is classification accuracy - the percentage of cases classified correctly.</p>

<p>But when we are using a neural network for classification, though <em>our</em> goal is classification accuracy, <em>that is not what the neural network is trying to optimize</em>. The neural network is still trying to optimize the real-valued cost function. Sometimes these point in the same direction, but sometimes they don't. In particular, I've been running into cases where a neural network trained to correctly minimize the cost function, has a classification accuracy worse than a simple hand-coded threshold comparison.</p>

<p>I've boiled this down to a minimal test case using TensorFlow. It sets up a perceptron (neural network with no hidden layers), trains it on an absolutely minimal dataset (one input variable, one binary output variable) assesses the classification accuracy of the result, then compares it to the classification accuracy of a simple hand-coded threshold comparison; the results are 60% and 80% respectively. Intuitively, this is because a single outlier with a large input value, generates a correspondingly large output value, so the way to minimize the cost function is to try extra hard to accommodate that one case, in the process misclassifying two more ordinary cases. The perceptron is correctly doing what it was told to do; it's just that this does not match what we actually want of a classifier. But the classification accuracy is not a continuous differentiable function, so we can't use it as the target for gradient descent.</p>

<p>How can we train a neural network so that it ends up maximizing classification accuracy?</p>

<pre><code>import numpy as np
import tensorflow as tf
sess = tf.InteractiveSession()
tf.set_random_seed(1)

# Parameters
epochs = 10000
learning_rate = 0.01

# Data
train_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 outputs
X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

# Weights
W = tf.Variable(tf.random_normal([cols]))
b = tf.Variable(tf.random_normal([]))

# Model
pred = tf.tensordot(X, W, 1) + b
cost = tf.reduce_sum((pred-Y)**2/rows)
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
tf.global_variables_initializer().run()

# Train
for epoch in range(epochs):
    # Print update at successive doublings of time
    if epoch&amp;(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 perceptron
classifications = [pred.eval({X: x}) &gt; 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 comparison
classifications = [x[0] &gt; 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))
</code></pre>