Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added new prob
  • Loading branch information
Jeet009 committed Sep 22, 2025
commit f62a64c5b337d1af060fd42a64f3d3ff99bcdc99
17 changes: 17 additions & 0 deletions questions/187_perceptron_trick_logistic_regression/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Problem

Implement the perceptron trick for logistic regression. Given training data with binary labels, update the weights using the perceptron learning rule and return the final weights and predictions.

The perceptron trick updates weights as follows:
- If prediction is correct: no update
- If prediction is wrong: $\mathbf{w} \leftarrow \mathbf{w} + \eta \cdot y_i \cdot \mathbf{x}_i$

Where:
- $\mathbf{w}$ is the weight vector (including bias)
- $\eta$ is the learning rate
- $y_i \in \{-1, +1\}$ is the true label
- $\mathbf{x}_i$ is the feature vector (with bias term)

The prediction function is: $\hat{y} = \text{sign}(\mathbf{w}^T \mathbf{x})$

Return the final weights and predictions on the training set.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"input": "X = [[1,1], [2,2], [3,3]]; y = [1, 1, -1]; learning_rate = 0.1; max_epochs = 10",
"output": "weights = [-0.2, -0.2, 0.0]; predictions = [1, 1, -1]",
"reasoning": "Perceptron algorithm updates weights only when predictions are wrong. For linearly separable data, it converges to a decision boundary that correctly classifies all training points. The final weights include the bias term as the last element."
}
57 changes: 57 additions & 0 deletions questions/187_perceptron_trick_logistic_regression/learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## Learning: Perceptron Trick in Logistic Regression

### Idea and formula
- **Goal**: Find a linear decision boundary that separates two classes using an iterative update rule.
- **Perceptron Learning Rule**: Update weights only when predictions are wrong.

The perceptron algorithm iteratively updates weights:

$$
\mathbf{w} \leftarrow \mathbf{w} + \eta \cdot y_i \cdot \mathbf{x}_i \quad \text{if } y_i \cdot (\mathbf{w}^T \mathbf{x}_i) \leq 0
$$

Where:
- $\mathbf{w}$ is the weight vector (including bias)
- $\eta$ is the learning rate
- $y_i \in \{-1, +1\}$ is the true label
- $\mathbf{x}_i$ is the feature vector (with bias term added)

### Intuition
- When prediction is correct ($y_i \cdot (\mathbf{w}^T \mathbf{x}_i) > 0$): no update needed
- When prediction is wrong ($y_i \cdot (\mathbf{w}^T \mathbf{x}_i) \leq 0$): adjust weights to make the correct prediction more likely
- The update rule "pulls" the decision boundary toward misclassified points

### Algorithm steps
1. Initialize weights $\mathbf{w} = \mathbf{0}$ (or small random values)
2. Add bias term to each feature vector: $\mathbf{x}_i \leftarrow [\mathbf{x}_i, 1]$
3. For each epoch:
- For each training example $(\mathbf{x}_i, y_i)$:
- Compute prediction: $\hat{y} = \text{sign}(\mathbf{w}^T \mathbf{x}_i)$
- If $y_i \cdot (\mathbf{w}^T \mathbf{x}_i) \leq 0$: update $\mathbf{w} \leftarrow \mathbf{w} + \eta \cdot y_i \cdot \mathbf{x}_i$
4. Repeat until convergence or max epochs

### Convergence guarantee
- If data is linearly separable, perceptron algorithm will converge in finite steps
- Convergence time depends on the "margin" of separation

### Worked example
Given 2D data: $X = [[1,1], [2,2], [3,3]]$, $y = [1, 1, -1]$, $\eta = 0.1$:

- Start: $\mathbf{w} = [0, 0, 0]$ (including bias)
- Add bias: $X = [[1,1,1], [2,2,1], [3,3,1]]$

Epoch 1:
- $(1,1,1)$: $\mathbf{w}^T \mathbf{x} = 0$, $y \cdot (\mathbf{w}^T \mathbf{x}) = 0 \leq 0$ → update
- $\mathbf{w} = [0,0,0] + 0.1 \cdot 1 \cdot [1,1,1] = [0.1, 0.1, 0.1]$
- $(2,2,1)$: $\mathbf{w}^T \mathbf{x} = 0.4$, $y \cdot (\mathbf{w}^T \mathbf{x}) = 0.4 > 0$ → no update
- $(3,3,1)$: $\mathbf{w}^T \mathbf{x} = 0.7$, $y \cdot (\mathbf{w}^T \mathbf{x}) = -0.7 \leq 0$ → update
- $\mathbf{w} = [0.1,0.1,0.1] + 0.1 \cdot (-1) \cdot [3,3,1] = [-0.2, -0.2, 0.0]$

Continue until convergence...

### Edge cases and tips
- **Linearly separable data**: Algorithm will converge
- **Non-separable data**: May not converge; use max epochs limit
- **Learning rate**: Too large may cause oscillation; too small may converge slowly
- **Initialization**: Starting from zero is common; random initialization can help
- **Bias handling**: Always add bias term as additional feature
15 changes: 15 additions & 0 deletions questions/187_perceptron_trick_logistic_regression/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"id": "187",
"title": "Perceptron Trick in Logistic Regression",
"difficulty": "medium",
"category": "Machine Learning",
"video": "",
"likes": "0",
"dislikes": "0",
"contributor": [
{
"profile_link": "https://github.com/Jeet009",
"name": "Jeet Mukherjee"
}
]
}
63 changes: 63 additions & 0 deletions questions/187_perceptron_trick_logistic_regression/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from typing import List, Tuple
import numpy as np


def perceptron_trick(X: List[List[float]], y: List[int], learning_rate: float = 0.1, max_epochs: int = 100) -> Tuple[List[float], List[int]]:
"""
Implement the perceptron trick for binary classification.

Args:
X: List of feature vectors (without bias term)
y: List of binary labels (-1 or +1)
learning_rate: Learning rate for weight updates
max_epochs: Maximum number of training epochs

Returns:
Tuple of (final_weights, predictions)
- final_weights: Weight vector including bias term
- predictions: Predictions on training data
"""
if not X or not y:
return [], []

n_features = len(X[0])
n_samples = len(X)

# Initialize weights (including bias term)
weights = [0.0] * (n_features + 1)

# Add bias term to each feature vector
X_with_bias = []
for x in X:
X_with_bias.append(x + [1.0]) # Add bias term

# Convert to numpy for easier computation
X_array = np.array(X_with_bias)
y_array = np.array(y)
weights_array = np.array(weights)

# Training loop
for epoch in range(max_epochs):
converged = True

for i in range(n_samples):
# Compute prediction: w^T * x
prediction = np.dot(weights_array, X_array[i])

# Check if prediction is wrong: y * (w^T * x) <= 0
if y_array[i] * prediction <= 0:
# Update weights: w = w + learning_rate * y * x
weights_array += learning_rate * y_array[i] * X_array[i]
converged = False

# If no updates were made, we've converged
if converged:
break

# Generate predictions on training data
predictions = []
for i in range(n_samples):
prediction = np.dot(weights_array, X_array[i])
predictions.append(1 if prediction > 0 else -1)

return weights_array.tolist(), predictions
21 changes: 21 additions & 0 deletions questions/187_perceptron_trick_logistic_regression/starter_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import List, Tuple
import numpy as np


def perceptron_trick(X: List[List[float]], y: List[int], learning_rate: float = 0.1, max_epochs: int = 100) -> Tuple[List[float], List[int]]:
"""
Implement the perceptron trick for binary classification.

Args:
X: List of feature vectors (without bias term)
y: List of binary labels (-1 or +1)
learning_rate: Learning rate for weight updates
max_epochs: Maximum number of training epochs

Returns:
Tuple of (final_weights, predictions)
- final_weights: Weight vector including bias term
- predictions: Predictions on training data
"""
# TODO: implement
raise NotImplementedError
26 changes: 26 additions & 0 deletions questions/187_perceptron_trick_logistic_regression/tests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"test": "from questions.187_perceptron_trick_logistic_regression.solution import perceptron_trick; w, pred = perceptron_trick([[1,1], [2,2], [3,3]], [1, 1, -1], 0.1, 10); print([round(x, 3) for x in w], pred)",
"expected_output": "[-0.2, -0.2, 0.0] [1, 1, -1]"
},
{
"test": "from questions.187_perceptron_trick_logistic_regression.solution import perceptron_trick; w, pred = perceptron_trick([[0,0], [1,1], [2,2]], [-1, -1, 1], 0.5, 5); print([round(x, 3) for x in w], pred)",
"expected_output": "[1.0, 1.0, 0.0] [-1, -1, 1]"
},
{
"test": "from questions.187_perceptron_trick_logistic_regression.solution import perceptron_trick; w, pred = perceptron_trick([[1], [2], [3]], [1, 1, -1], 0.1, 20); print([round(x, 3) for x in w], pred)",
"expected_output": "[-0.2, 0.0] [1, 1, -1]"
},
{
"test": "from questions.187_perceptron_trick_logistic_regression.solution import perceptron_trick; w, pred = perceptron_trick([[0,0], [1,0], [0,1], [1,1]], [1, 1, 1, -1], 0.1, 50); print([round(x, 3) for x in w], pred)",
"expected_output": "[-0.1, -0.1, 0.0] [1, 1, 1, -1]"
},
{
"test": "from questions.187_perceptron_trick_logistic_regression.solution import perceptron_trick; w, pred = perceptron_trick([[1,2], [3,4], [5,6]], [1, 1, -1], 0.01, 100); print([round(x, 3) for x in w], pred)",
"expected_output": "[-0.04, -0.04, 0.0] [1, 1, -1]"
},
{
"test": "from questions.187_perceptron_trick_logistic_regression.solution import perceptron_trick; w, pred = perceptron_trick([], [], 0.1, 10); print(w, pred)",
"expected_output": "[] []"
}
]