Article preview
Artificial Intelligence
Machine Learning
14 июля
15 minutes

Logistic Regression: Don't Mix It Up

In this article, we will continue exploring the classification problem. We'll figure out what decision trees can't do, how to fix these shortcomings, and how to put regression to work solving a classification task.

Vyacheslav Gorash avatar

Vyacheslav Gorash

3D Graphics and Machine Learning Developer with 6 years of experience

This article is part of a series on the fundamentals of machine learning.

In the previous article, we started exploring the classification problem. We covered what is probably the most intuitively understandable algorithm — decision trees. In this article, we'll look at applying regression to classification problems.

What's Wrong with Trees?

When reading the previous article, it might have seemed that decision trees are the perfect option for classification. Indeed, they can find complex dependencies, their internal logic is understandable, and the training process is reasonably fast. But in reality, they're not perfect. There are several fundamental drawbacks.

First of all — overfitting. A tree memorizes small details too well, but at the same time fails to grasp deeper patterns. As a result, the tree shows excellent performance on familiar data, but quality drops sharply as soon as it encounters data that wasn't in the training set. In the end, we have to artificially limit the tree's growth, which degrades overall performance, but it's the only way to keep the tree from simply memorizing the correct answers and instead push it to solve the problem in general terms.

Second, a tree is a greedy algorithm. It makes the best decision for each step, not for the problem as a whole. And since at some point tree construction is forcibly terminated to combat overfitting, this leads to an incomplete solution.

And third, the tree has fundamental limitations on the types of dependencies. It works very well with separate "islands" of data, but handles continuous dependencies extremely poorly. This is because the tree splits data along the coordinate axes.

Tree problems 1

Tree problems 2

The figures above show a successful and an unsuccessful case of data distribution. In the first figure, everything is fine — just three splits. But in the second one, there are already seven. Although, if we look at the data, we can see that a single diagonal line would have been enough, but a decision tree can't do that — it builds splits only along the axes.

What Can We Do?

In fact, there are several ways to deal with the shortcomings of decision trees. The first group of methods involves improving them. One option is to use not one tree, but several. Moreover, during training, each tree learns on its own subset of the data. As a result, we get a set (a "forest") of different trees. And when we need to produce an answer, we show the data to each tree separately, and then hold a vote. The winning result is what we output. This approach (called a "random forest") significantly reduces the impact of overfitting. This method is still used in some cases even now, in the era of neural network dominance.

But everything comes at a price. Here, for the most part, we pay with a loss of intuitiveness. Indeed, while a single tree's decisions are fairly understandable (you can look at its structure and see why it produced a particular answer), understanding the voting logic of many trees is much harder. Plus, the limitations regarding distributions and dependencies between data don't go away.

The second way to solve the problem is to move away from decision trees and similar algorithmic models.

What About Regression?

Let's recall how linear regression works. It works very simply: each input value is multiplied by the corresponding weight, all these products are summed, a bias is added to the sum, and that's the result:

y= ∑i=0nkixi+by = \ \sum_{i = 0}^{n}{{k_{i}x}_{i} + b}

Let's try to use the same approach for classification. What stops us from doing so? First of all — the range of output values. This is regression, which means the model's output is just any number. But we're solving a classification problem and expect only two values: 0 or 1.

The first thing that comes to mind is to simply set a threshold. For example, if the output value is positive, that's 1, if negative — 0. But the threshold approach would be extremely unstable. For example, minus one thousandth gives one answer, and one thousandth — already another. This means all output values would be concentrated very close to zero, and the slightest change in the input data could affect the result. Another problem is that the threshold function is not differentiable, meaning it has no derivative. Derivatives are very important when training any regression models, so the absence of a derivative is a very serious problem.

In fact, the best way to use regression for classification is to somehow map values from the range [−∞, +∞]\lbrack - \infty,\ + \infty\rbrack to the range [0, 1]\lbrack 0,\ 1\rbrack. Why would this work, since we need only two values rather than a range? The thing is, a continuous value from the [0, 1]\lbrack 0,\ 1\rbrack range can be interpreted as the probability of one of the answers. For example, if we get a value of 0.6, we can interpret it as a 60% probability of answer 1.

This is also nice because we can set the threshold ourselves depending on the task. For example, imagine a system that automatically buys stocks if it considers them profitable. In such a situation, we'd like to avoid unnecessary risk, so the threshold can be set higher, say 90%. This means the system will only buy stocks if it's very confident about their profitability.

In other situations, on the contrary, it's better to set a lower threshold. Imagine a system that monitors bank transfers and checks whether they're fraudulent. In this case, it's better to make the system more sensitive. For example, so that it triggers at 30% confidence that the operation is fraudulent. At the same time, it doesn't have to immediately block the transfer. The decision can be passed to a human. This way, nothing important will be missed, and a human will make the final decision.

It remains to figure out how to perform the necessary range conversion. Such a conversion must be smooth, without discontinuities or jumps, so that it doesn't interfere with computing derivatives. In fact, there are many functions that can help with this, but the most common one is the sigmoid, or logistic function, which gave this classification method its name.

Figuring Out the Sigmoid

The function itself looks very simple:

σ(x)= 11+e−x\sigma(x) = \ \frac{1}{1 + e^{- x}}

If the value of xx tends to minus infinity, the denominator of the fraction, on the contrary, shoots off to infinity, which makes the function's value very close to zero. For very large values of xx, the value of e−xe^{- x} tends to zero, the denominator is close to one, as is the final value of the function. As a result, its graph looks like this:

Sigmoid graph

As you can see, the function smoothly and without discontinuities converts any input value into a range from zero to one.

Now all we have to do is apply this function to the regression result and get the probability of a positive answer:

y^=σ(∑i=0nkixi+b)\widehat{y} = \sigma \left( \sum_{i = 0}^{n}{k_{i}x_{i}} + b \right)

Just like in ordinary linear regression, we now need to compute all the coefficients kik_{i} and the bias bb.

Training

Any training is the minimization of a loss function between the predicted result and the real one:

L(y, y^)→0L(y,\ \widehat{y}) \rightarrow 0

More precisely, we minimize the function not for a single pair of values, but the sum across all pairs:

∑i=1mL(y, y^)→0\sum_{i = 1}^{m}{L\left( y,\ \widehat{y} \right) \rightarrow 0}

For linear regression, where the results were continuous, the loss function was simply the squared difference:

l=(y−y^)2{l = \left( y - \widehat{y} \right)}^{2}

Here, however, our result is discrete (only 0 or 1). Therefore, we need to use a different function.

At this point, a question may arise: why can't we use the squared difference? After all, our output is a probability, that is, an ordinary number. This question is hard to answer right now, since we don't yet know the details of the training process. For now, we can simply say that when using the squared difference, training can happen, but it will be slow, and the result may be rather mediocre.

A different function is used for the classification problem — namely, binary cross-entropy. This function looks more complex than the simple squared difference:

l=−(ylog⁡(y^)+(1−y)log⁡(1−y^))l = - \left( y\log \left( \widehat{y} \right) + \left(1 - y\right) \log{ (1 - \widehat{y} ) } \right)

Despite its apparent complexity, cross-entropy works much better for the classification problem. In the end, we need to make the sum of the functions for each pair of values as small as possible. To do this, we need to find the right coefficients kik_{i} and bb.

And here comes the not-so-good news. The thing is, while we could train simple linear regression very quickly by solving a single system of equations, there's no analytical solution here. This is because our formulas are now nonlinear. So we'll have to look for other ways to find the required coefficients. Such methods exist, and there are several. We'll look at the most popular one — gradient descent.

Let's simplify the model as much as possible. We'll keep, just like in the article on regression, only one input variable xx and two coefficients: kk and bb. We'll compute the answer using the formula:

y^= σ(kx+b)\widehat{y} = \ \sigma(kx + b)

or

y^= 11+e−(kx+b)\widehat{y} = \ \frac{1}{1 + e^{- (kx + b)}}

Now let's write out the loss function:

l=−(ylog⁡(y^)+(1−y)log⁡(1−y^) )l = - \left( y\log\left( \widehat{y} \right) + (1 - y)\log{(1 - \widehat{y})\ } \right)l=−(y∙log(11+e−(kx+b))+(1−y)∙log(1− 11+e−(kx+b)))l = - \left( y \bullet log\left( \frac{1}{1 + e^{- (kx + b)}} \right) + (1 - y) \bullet log\left( 1 - \ \frac{1}{1 + e^{- (kx + b)}} \right) \right)

As mentioned above, we minimize not the loss function, but the sum of the loss functions across all our data:

−∑i=1myi∙log⁡(11+e−(kxi+b))+(1−yi)∙log(1− 11+e−(kxi+b)) →0- \sum_{i = 1}^{m}{y_{i} \bullet \log\left( \frac{1}{1 + e^{- \left( kx_{i} + b \right)}} \right) + \left( 1 - y_{i} \right) \bullet log\left( 1 - \ \frac{1}{1 + e^{- \left( kx_{i} + b \right)}} \right)\ \rightarrow 0}

The final expression looks rather intimidating, and as already mentioned, can't be optimized analytically. So the main idea here is not to try to reach the minimum at once, but to take many small steps, gradually approaching the minimum.

Traveling Across Surfaces

If we visualize the loss function, we'll see a long, narrow trough. The bottom of this trough is the minimum of the loss function:

Loss function visualization

Notice — the minimum here is not a point, but a line. This means there are many pairs of kk and bb that work for us. We just need to find any of them.

We need to descend from the wall of the trough (the random place where we start training) to its bottom. Essentially, we need to follow this algorithm:

  1. Find the direction of movement toward the minimum
  2. Take a small step toward the minimum (change kk and bb by some amount)
  3. Repeat steps 1 and 2 until we reach the minimum

This algorithm contains the main difference from what we used for ordinary linear regression: there, we immediately found the parameters analytically for which the loss function is at the minimum. Here, we start with random values and gradually change the parameters, approaching the solution. That is, this method is iterative — we repeatedly perform the same actions, gradually improving the result. It remains to figure out how to find the direction to the minimum.

And this is where derivatives take center stage. If we recall their properties, we'll see that the partial derivative with respect to one parameter shows how quickly the function grows with a small increase in that parameter. And here's the key idea: we need to take a step in the opposite direction from the growth. What if there are multiple variables? In that case, we must compute the derivative with respect to each variable and change each variable against the growth of its derivative. In our case, mathematically this looks like this:

k=k−ρ∂L∂k,  b=b−ρ∂L∂bk = k - \rho\frac{\partial L}{\partial k},\ \ b = b - \rho\frac{\partial L}{\partial b}

The parameter ρ\rho in this expression is simply a small number (for example, 0.01) that we add to keep the step small. Since this number controls the speed of the steps, and therefore the speed of training, it's called the learning rate. I'll explain later how to choose such a number and what else it affects besides the training speed.

We proceed similarly if there are more than two parameters. In this case, we simply compute the derivatives with respect to each parameter. A vector of such derivatives is called a gradient, hence the name of the method — gradient descent. The "descent" refers to the movement toward the function's minimum.

It remains to figure out how to know that we've already reached the minimum. Here we might recall that at the minimum, the derivatives with respect to all parameters become zero and we stop. In practice, this approach usually doesn't work. The thing is, most of the time, gradient descent overshoots the minimum point, then comes back, overshoots the minimum again, now in the opposite direction, and so on. So usually, the change in derivatives is judged less strictly. Most often, we don't even look at the derivatives, but at the loss function LL itself. If after another step it has barely changed (ΔL< ε\mathrm{\Delta}L < \ \varepsilon), we consider that we're very close to the minimum and training can be stopped. Again, there are many nuances, but they'll be covered in a separate article.

Computing Derivatives

Now that we've figured out how to train a logistic regression, it remains only to compute the derivatives.

We need to compute the derivatives of the loss function with respect to two parameters. But, as we've already seen, the final expression is rather intimidating, and computing the derivative will be quite difficult. So it's reasonable to break the computation into parts. Let's look at what the final function consists of.

The process of multiplying the input data by weights and adding the bias is called the summation function. Let's denote it with the letter zz:

z(x)=kx+bz(x) = kx + b

Next, we apply the sigmoid to this result. This process is called the activation function. Let's denote it with the letter aa:

a(z)=11+e−za(z) = \frac{1}{1 + e^{- z}}

And finally, we compute the loss function between our prediction and the correct answer. Let's denote the function itself as LL. In our case:

L(a, y)=−(ylog⁡(a)+(1−y)log⁡(1−a))L(a,\ y) = - \left( y \log(a) + \left(1 - y\right) \log{ \left(1 - a \right) } \right)

We need to compute two derivatives: ∂L∂k\frac{\partial L}{\partial k} and ∂L∂b\frac{\partial L}{\partial b}. And here, just like in the article on linear regression, we can use the chain rule:

∂L∂k= ∂L∂a∙∂a∂z∙∂z∂k\frac{\partial L}{\partial k} = \ \frac{\partial L}{\partial a} \bullet \frac{\partial a}{\partial z} \bullet \frac{\partial z}{\partial k}

Similarly for bb. Agree, it's much more convenient to compute derivatives in parts than as a whole.

Let's start computing from the last function:

∂L∂a= −(y∙1a+((1−y)∙11−a∙−1))=−(ya−1−y1−a)=1−y1−a−ya\frac{\partial L}{\partial a} = \ - \left( y \bullet \frac{1}{a} + \left( (1 - y) \bullet \frac{1}{1 - a} \bullet - 1 \right) \right) = - \left( \frac{y}{a} - \frac{1 - y}{1 - a} \right) = \frac{1 - y}{1 - a} - \frac{y}{a}

Let's additionally combine into a common denominator and simplify:

1−y1−a−ya= a−ay−y+aya(1−a)=a−ya(1−a)\frac{1 - y}{1 - a} - \frac{y}{a} = \ \frac{a - ay - y + ay}{a(1 - a)} = \frac{a - y}{a(1 - a)}

Now let's compute the derivative of the sigmoid activation:

∂a∂z= ∂∂z 11+e−z=−1(1+e−z)2 ∙−e−z=e−z(1+e−z)2\frac{\partial a}{\partial z} = \ \frac{\partial}{\partial z}\ \frac{1}{1 + e^{- z}} = - \frac{1}{\left( 1 + e^{- z} \right)^{2}}\ \bullet - e^{- z} = \frac{e^{- z}}{\left( 1 + e^{- z} \right)^{2}}

It seems there's nothing more to simplify, but here we can pull off a very elegant mathematical trick. Let's split the fraction into two:

e−z(1+e−z)2= 11+e−z∙e−z1+e−z\frac{e^{- z}}{\left( 1 + e^{- z} \right)^{2}} = \ \frac{1}{1 + e^{- z}} \bullet \frac{e^{- z}}{1 + e^{- z}}

What's the first fraction? It's our original function! So we can make this transformation:

∂a∂z=a∙e−z1+e−z\frac{\partial a}{\partial z} = a \bullet \frac{e^{- z}}{1 + e^{- z}}

But that's not all. Let's transform the second part:

e−z1+e−z= ez∙e−zez∙(1+e−z)= 11+ez\frac{e^{- z}}{1 + e^{- z}} = \ \frac{e^{z} \bullet e^{- z}}{e^{z} \bullet (1 + e^{- z})} = \ \frac{1}{{1 + e}^{z}}

One more trick. Let's add and immediately subtract eze^{z} from the numerator:

11+ez=1+ez−ez1+ez= 1+ez1+ez− ez1+ez=1−ez1+ez\frac{1}{{1 + e}^{z}} = \frac{1 + e^{z} - e^{z}}{{1 + e}^{z}} = \ \frac{1 + e^{z}}{1 + e^{z}} - \ \frac{e^{z}}{1 + e^{z}} = 1 - \frac{e^{z}}{1 + e^{z}}

Now the final transformation. Let's divide the fraction by eze^{z}. This is equivalent to multiplying the numerator and denominator by e−ze^{- z}:

1−ez1+ez=1−ez∙e−z(1+ez)∙e−z=1− 1e−z+1=1− 11+e−z1 - \frac{e^{z}}{1 + e^{z}} = 1 - \frac{e^{z} \bullet e^{- z}}{\left( 1 + e^{z} \right) \bullet e^{- z}} = 1 - \ \frac{1}{e^{- z} + 1} = 1 - \ \frac{1}{1 + e^{- z}}

What do we see? Our original function again! As a result, the derivative of the sigmoid is fully expressed through the sigmoid itself:

∂a∂z=a∙e−z1+e−z=a∙(1− 11+e−z)=a(1−a)\frac{\partial a}{\partial z} = a \bullet \frac{e^{- z}}{1 + e^{- z}} = a \bullet \left( 1 - \ \frac{1}{1 + e^{- z}} \right) = a\left(1 - a\right)

But the beauty doesn't end there. Let's now compute the product of the two derivatives we've already found:

∂L∂a∙∂a∂z= a−ya(1−a)∙a(1−a)=a−y\frac{\partial L}{\partial a} \bullet \frac{\partial a}{\partial z} = \ \frac{a - y}{a(1 - a)} \bullet a(1 - a) = a - y

In the end, from a bulky expression, we're left with a tiny line. And we're almost ready to compute the final derivatives. All that's left is to compute the derivative of the summation function with respect to kk and bb:

∂z∂k= ∂∂kkx+b=x,  ∂z∂b= ∂∂bkx+b=1\frac{\partial z}{\partial k} = \ \frac{\partial}{\partial k}kx + b = x,\ \ \frac{\partial z}{\partial b} = \ \frac{\partial}{\partial b}kx + b = 1

Everything is ready. The final formulas for the derivatives:

∂L∂k=x(a−y),  ∂L∂b=a−y\frac{\partial L}{\partial k} = x(a - y),\ \ \frac{\partial L}{\partial b} = a - y

Another very nice thing is that if instead of one-dimensional input data we take multidimensional data, almost nothing changes in the formulas. We just need to compute a few more derivatives using the formula:

∂L∂ki=xi(a−y)\frac{\partial L}{\partial k_{i}} = x_{i}\left(a - y\right)

The Long-Awaited Descent

Now we have everything we need to derive the training algorithm. Initially, we initialize the coefficients kk and bb with random numbers. Usually, the numbers are quite small, in the range from zero to one hundredth. Then the training process itself begins:

  1. For each pair of input data and correct answer, compute the derivatives with respect to all parameters kk and bb
  2. Once computed for all examples, average each derivative, that is, sum the derivatives for each example and divide by the number of examples
  3. Take a gradient descent step: ki=k− ρ∙1n∙∑xij(aj−yj)k_{i} = k - \ \rho \bullet \frac{1}{n} \bullet \sum_{}^{}{x_{ij}\left( a_{j} - y_{j} \right)}. Similarly for b:b=b−ρ∙1n∙∑(aj−yj)b:b = b - \rho \bullet \frac{1}{n} \bullet \sum_{}^{}\left( a_{j} - y_{j} \right)
  4. Repeat steps 1-3 until we reach the minimum (or rather, end up next to it)

After training is complete, our model is ready for use. To do this, we first compute the weighted sum:

z= ∑i=1nkixi+bz = \ \sum_{i = 1}^{n}{k_{i}x_{i} + b}

And then apply the sigmoid:

y^=11+e−z\widehat{y} = \frac{1}{1 + e^{- z}}

The final value y^\widehat{y} is the probability that the object's class is 1. We can interpret this probability, as mentioned above, in different ways depending on the task.

Problems Again

Despite the fact that logistic regression seems like a very good option, it's also not without problems. On the one hand, yes, it's better than decision trees in terms of overfitting. But on the other hand, it has the exact opposite problem. Recall that decision trees worked very well with separate "islands" of data but handled continuous distributions poorly. Here, the situation is reversed.

Logistic regression draws exactly one separation line, albeit at any angle. Thus, if the data can be separated by a single line, logistic regression will work perfectly. But if such a separation is impossible, training won't succeed in any case.

Logistic regression 1

Logistic regression 2

Figure 2 shows a good situation for regression — the data is linearly separable. But in the first case, no matter how we draw the line, some objects will always be misclassified.

What should we do in this case? Ideally, we'd like to draw a single line, like in logistic regression, but not a straight one — one of arbitrary shape. This would allow us to separate any data, solving the problems of both logistic regression and decision trees.

Logistic regression 3

And there's a way to do this. We just need to combine several logistic regressions: the outputs of some should be the inputs of others. Perhaps someone has already recognized this construction as a neural network. But I'll talk about neural networks and how they're trained in the next article.

To be continued…

Questions and Answers

Ready to discuss your project?

Describe your task, we will make a research and respond to you as soon as possible.

We will be happy to advise you in any of the available ways.

By leaving a request you agree to the data processing policy