0Pricing
Machine Learning Academy · 강의

회귀에서 분류로: 임계값에 따른 결정

선형 회귀가 이진 결과에 적합하지 않은 이유를 이해하고, 임계값을 추가해 점수를 클래스 레이블로 변환하는 방식을 살펴봅니다.

회귀에서 분류로: 임계값에 따른 결정은(는) CoddyKit의 무료 Machine Learning Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Machine Learning Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Machine Learning Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

What Is Classification?

Classification is the supervised learning task of predicting which category an input belongs to, rather than predicting a continuous number. Examples include:

  • Predicting whether an email is spam or not-spam (binary).
  • Predicting which digit (0-9) is in an image (multi-class).
  • Predicting which disease a patient has given symptoms (multi-class).

The key distinction from regression: the output is a discrete label, not a real number. This seemingly small change requires different algorithms, different loss functions, and different evaluation metrics.

Why Linear Regression Fails for Classification

A tempting approach is to encode class 0 and class 1 as numbers and apply linear regression. For example, encode 'not spam' as 0 and 'spam' as 1, then train a linear regressor. The immediate problem: linear regression produces outputs anywhere from -∞ to +∞, but probabilities must be between 0 and 1. A prediction of 1.7 for class membership is meaningless.

A second problem: linear regression tries to pull the best-fit line through all examples. Adding a clear outlier far from the boundary can rotate the line so it misclassifies many previously-correct examples. The loss function is fundamentally wrong for binary outcomes.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# Binary dataset: 1 = spam, 0 = not spam
word_count = np.array([10, 20, 25, 30, 40, 50, 200]).reshape(-1, 1)
spam = np.array([0, 0, 0, 1, 1, 1, 1])

model = LinearRegression()
model.fit(word_count, spam)

# Problematic: predictions outside [0,1]
predictions = model.predict([[5], [25], [200]])
print('Linear regression predictions (should be 0 or 1):')
print(predictions)  # might be negative or >1

Threshold Decisions: Converting Scores to Labels

The simplest approach to classification is to use a regression model's output as a score and apply a threshold to convert it to a binary label. If the score is above the threshold, predict class 1; below it, predict class 0.

With a linear regression output, you might choose a threshold of 0.5: anything above 0.5 is spam, below is not-spam. This is called a threshold classifier. While crude, it illustrates the key concept: scores must be converted to decisions, and the choice of threshold involves a trade-off between different types of errors.

import numpy as np
from sklearn.linear_model import LinearRegression

X = np.array([10, 20, 25, 30, 40, 50]).reshape(-1, 1)
y = np.array([0, 0, 0, 1, 1, 1])

model = LinearRegression()
model.fit(X, y)

# Apply threshold to convert scores to labels
X_new = np.array([[15], [28], [45]])
scores = model.predict(X_new)
threshold = 0.5
labels = (scores >= threshold).astype(int)

for x, score, label in zip(X_new.ravel(), scores, labels):
    print(f'x={x}: score={score:.2f} -> label={label}')

The Problems with a Fixed Threshold

Choosing a threshold of 0.5 is arbitrary. The optimal threshold depends on the relative cost of different error types:

  • A false positive (predict spam when it is not) means a legitimate email lands in the spam folder.
  • A false negative (predict not-spam when it is spam) means junk email reaches the inbox.

In medical diagnosis, the costs are much more asymmetric: a false negative (missing a real disease) may be catastrophic, so you lower the threshold to catch more true positives even at the cost of more false alarms. The threshold is a business decision, not a mathematical one.

import numpy as np

# Same scores, different thresholds give different label distributions
scores = np.array([0.2, 0.45, 0.55, 0.7, 0.85, 0.95])
y_true = np.array([0, 0, 1, 1, 1, 1])

for threshold in [0.3, 0.5, 0.7]:
    labels = (scores >= threshold).astype(int)
    correct = (labels == y_true).sum()
    print(f'Threshold {threshold}: labels={labels.tolist()}, correct={correct}/{len(y_true)}')

Binary vs Multi-Class Classification

Binary classification has exactly two possible output classes (spam/not-spam, disease/healthy, fraud/legitimate). Multi-class classification has three or more classes (which digit 0-9, which species of flower, which product category).

Most binary classifiers extend to multi-class through two strategies:

  • One-vs-Rest (OvR): train one binary classifier per class, predict the class whose classifier is most confident.
  • One-vs-One (OvO): train a binary classifier for every pair of classes, take a majority vote.

Scikit-learn handles this automatically for most algorithms.

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# Iris has 3 classes: sklearn handles multi-class automatically
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

model = LogisticRegression(multi_class='ovr', max_iter=200)
model.fit(X_train, y_train)
print('Test accuracy:', model.score(X_test, y_test).round(3))
print('Classes:', model.classes_)

Decision Boundaries: Where the Model Decides

A classifier divides the feature space into regions, one for each class. The boundary between regions is called the decision boundary. For a linear classifier, the decision boundary is a straight line (in 2D), a plane (in 3D), or a hyperplane (in higher dimensions).

The location and shape of the decision boundary is what the algorithm learns during training. Visualising the decision boundary on a 2D dataset is one of the best ways to build intuition for how a classifier works and why its predictions are correct or wrong in specific regions of the feature space.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

X, y = make_classification(n_samples=100, n_features=2, n_redundant=0, random_state=42)
model = LogisticRegression()
model.fit(X, y)

# Plot decision boundary
xx, yy = np.meshgrid(np.linspace(-3, 3, 200), np.linspace(-3, 3, 200))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)

plt.contourf(xx, yy, Z, alpha=0.3)
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', alpha=0.8)
plt.title('Linear Decision Boundary')
plt.show()

Accuracy: The Most Intuitive Metric

Accuracy is the fraction of predictions that are correct: accuracy = correct_predictions / total_predictions. It is the most intuitive metric and appropriate when classes are balanced and all errors are equally costly.

However, accuracy is a trap for imbalanced datasets. If 95% of emails are legitimate and your model predicts 'not spam' for everything, it achieves 95% accuracy while completely failing at its job — never catching a single spam email. This is the accuracy paradox, and it motivates more informative metrics like precision and recall.

from sklearn.metrics import accuracy_score
import numpy as np

# Balanced dataset
y_true_balanced = np.array([0, 1, 0, 1, 0, 1, 0, 1])
y_pred_balanced = np.array([0, 1, 0, 0, 0, 1, 1, 1])
print(f'Balanced accuracy: {accuracy_score(y_true_balanced, y_pred_balanced):.2f}')  # 0.75

# Imbalanced: 95% class 0
y_true_imb = np.array([0]*95 + [1]*5)
y_pred_always0 = np.zeros(100, dtype=int)
print(f'Imbalanced accuracy (always predict 0): {accuracy_score(y_true_imb, y_pred_always0):.2f}')  # 0.95!

The Predict vs Predict_proba Distinction

Most scikit-learn classifiers provide two prediction methods:

  • predict(X) — returns the hard class label after applying the default threshold (usually 0.5 for binary classification).
  • predict_proba(X) — returns a probability array of shape (n_samples, n_classes), giving the model's confidence in each class.

Using predict_proba gives you much more control because you can apply any threshold. This is essential for business applications where the optimal threshold is not 0.5.

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np

X, y = make_classification(n_samples=200, n_features=5, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model = LogisticRegression()
model.fit(X_train, y_train)

# Hard labels
hard_labels = model.predict(X_test[:5])
print('Hard labels:', hard_labels)

# Probabilities
probas = model.predict_proba(X_test[:5])
print('Probabilities (class 0, class 1):')
for row in probas:
    print(f'  Not spam: {row[0]:.2f}  |  Spam: {row[1]:.2f}')

Custom Thresholds in Practice

Applying a custom threshold to probability outputs lets you tune the trade-off between false positives and false negatives without retraining the model. This is called threshold moving and is a common post-processing step in production systems.

Lower threshold → model flags more examples as positive → more true positives but also more false positives. Higher threshold → more conservative → fewer false positives but more false negatives. The right threshold is determined by the cost of each error type in your specific application.

import numpy as np
from sklearn.metrics import confusion_matrix

# Get probability scores for positive class
spam_probas = model.predict_proba(X_test)[:, 1]

print('Confusion matrices at different thresholds:')
for threshold in [0.3, 0.5, 0.7]:
    y_pred = (spam_probas >= threshold).astype(int)
    cm = confusion_matrix(y_test, y_pred)
    tp = cm[1, 1]
    fp = cm[0, 1]
    fn = cm[1, 0]
    tn = cm[0, 0]
    print(f'\nThreshold {threshold}: TP={tp} FP={fp} FN={fn} TN={tn}')

Common Classification Algorithms Overview

Linear regression with a threshold is just the beginning. Scikit-learn provides many dedicated classification algorithms, each with strengths and weaknesses:

  • Logistic Regression — the proper probabilistic linear classifier (next lesson).
  • K-Nearest Neighbors — classifies by majority vote of nearest training examples.
  • Decision Trees — rule-based, fully interpretable.
  • Random Forest — ensemble of trees, robust and powerful.
  • SVM — finds the maximum-margin hyperplane.
  • Naive Bayes — fast, probabilistic, excellent for text.
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score

X, y = load_breast_cancer(return_X_y=True)

for name, clf in [('Logistic Regression', LogisticRegression(max_iter=1000)),
                  ('Decision Tree', DecisionTreeClassifier()),
                  ('KNN', KNeighborsClassifier()),
                  ('Random Forest', RandomForestClassifier())]:
    score = cross_val_score(clf, X, y, cv=5, scoring='accuracy').mean()
    print(f'{name}: {score:.3f}')

Quick Check

Test your understanding of Machine Learning with Python concepts from this lesson.

Lesson Recap

In this lesson you learned: linear regression fails for binary classification because it produces unbounded outputs that cannot represent probabilities, applying a threshold converts probability scores to class labels with a tunable trade-off between false positives and false negatives, and accuracy is misleading for imbalanced datasets — recall and precision provide a more complete picture. Next up we study logistic regression — the proper probabilistic linear classifier that uses the sigmoid function to produce calibrated probabilities.

자주 묻는 질문

“회귀에서 분류로: 임계값에 따른 결정” 강의는 무료인가요?

네 — “회귀에서 분류로: 임계값에 따른 결정” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Machine Learning Academy 강의 전체를 잠금 해제할 수 있습니다. Machine Learning Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“회귀에서 분류로: 임계값에 따른 결정”에서 뭘 배우나요?

선형 회귀가 이진 결과에 적합하지 않은 이유를 이해하고, 임계값을 추가해 점수를 클래스 레이블로 변환하는 방식을 살펴봅니다. 브라우저에서 직접 실행하는 실습 코드로 Machine Learning Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Machine Learning Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Machine Learning Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“회귀에서 분류로: 임계값에 따른 결정” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Machine Learning Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Machine Learning Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 회귀에서 분류로: 임계값에 따른 결정
  2. 로지스틱 회귀와 시그모이드 함수
  3. 혼동 행렬 이해하기
  4. 실전에서의 정밀도, 재현율, F1 점수
← Machine Learning Academy(으)로 돌아가기