Home [PyTorch] 로지스틱 회귀 구현하기
Post
Cancel

[PyTorch] 로지스틱 회귀 구현하기

Logistic Regression 구현하기

1
2
3
4
5
6
import numpy as np
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.nn.functional as F

Data

1
2
3
4
5
6
7
8
9
10
np.random.seed(42)

train_X = np.random.randn(1000)
train_X[:500] = train_X[:500] + 4
train_y = np.array([1]*500 + [0]*500)

ids = np.arange(1000)
np.random.shuffle(ids)
train_X = train_X[ids]
train_y = train_y[ids]
1
_ = plt.scatter(train_X, train_y)

png

Implement

1
2
3
4
# reset 0 weights, bias
w = torch.zeros(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
print(f"Early w: {w}, b: {b}")
1
Early w: tensor([0.], requires_grad=True), b: tensor([0.], requires_grad=True)
1
2
# optimizer - 확률적 경사 하강법
optimizer = torch.optim.SGD([w, b], lr=0.2)
1
train_X, train_y = torch.FloatTensor(train_X), torch.FloatTensor(train_y)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Approximation
e, l = [], []
for epoch in range(1001):
    # Loss - binary cross entropy
    appr = torch.sigmoid(w*train_X+b)
    loss = F.binary_cross_entropy(appr, train_y)
    # Updates Params
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    # Print and Collect Log
    e.append(epoch)
    l.append(loss.detach().numpy())
    if epoch%100==0:
        print(f"Epochs: {epoch:5d} - Loss: {loss:.5f}")
1
2
3
4
5
6
7
8
9
10
11
Epochs:     0 - Loss: 0.69315
Epochs:   100 - Loss: 0.15076
Epochs:   200 - Loss: 0.10955
Epochs:   300 - Loss: 0.09282
Epochs:   400 - Loss: 0.08355
Epochs:   500 - Loss: 0.07758
Epochs:   600 - Loss: 0.07339
Epochs:   700 - Loss: 0.07027
Epochs:   800 - Loss: 0.06786
Epochs:   900 - Loss: 0.06592
Epochs:  1000 - Loss: 0.06434
1
2
3
plt.plot(e, l)
plt.title("Epochs - Loss")
plt.show()

png

1
print(f"After Approximate w: {w}, b: {b}")
1
After Approximate w: tensor([2.6216], requires_grad=True), b: tensor([-5.1346], requires_grad=True)
1
2
3
plt.scatter(train_X, train_y)
plt.scatter(train_X, appr.detach().numpy())
plt.show()

png

서포터님이 구현한 방식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Reference: https://smwgood.tistory.com/6

from torch.autograd import Function

class Sigmoid(Function):

    @staticmethod
    def forward(ctx, logits):
        probs = 1 / (1 + torch.exp(-logits))
        ctx.save_for_backward(probs)
        return probs

    @staticmethod
    def backward(ctx, *grad_outputs):
        probs, = ctx.saved_tensors
        return probs * (1-probs)


class CrossEntropyWithLogits(Function):

    @staticmethod
    def forward(ctx, logits, targets):
        probs = 1 / (1 + torch.exp(-logits))
        ctx.save_for_backward(probs, targets)
        return -( targets*torch.log(probs) + (1-targets)*torch.log(1-probs) ).mean()

    @staticmethod
    def backward(ctx, *grad_outputs):
        probs, targets = ctx.saved_tensors
        grad_input = probs - targets
        return grad_input, None
1
2
3
logits  = torch.randn(10, requires_grad=True)
targets = (torch.rand(10) > 0.5).to(dtype=torch.long)
logits, targets
1
2
3
(tensor([-0.0578, -0.2759,  1.4966, -1.6991,  0.7557,  0.5992,  1.1118, -0.6826,
          0.1755, -0.0991], requires_grad=True),
 tensor([0, 0, 1, 0, 0, 1, 1, 1, 0, 1]))
1
2
sigmoid = Sigmoid.apply
cross_entropy_with_logits = CrossEntropyWithLogits.apply
1
2
probs = sigmoid(logits)
probs
1
2
tensor([0.4855, 0.4315, 0.8171, 0.1546, 0.6804, 0.6455, 0.7525, 0.3357, 0.5438,
        0.4752], grad_fn=<SigmoidBackward>)
1
2
loss = cross_entropy_with_logits(logits, targets)
loss
1
tensor(0.6082, grad_fn=<CrossEntropyWithLogitsBackward>)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
lr = 0.001

for epoch in range(100):
    logits = a + b * train_X
    loss = cross_entropy_with_logits(logits, train_y)
    loss.backward()

    with torch.no_grad():
        a -= lr * a.grad
        b -= lr * b.grad

        a.grad.zero_()
        b.grad.zero_()

    if epoch % 10 == 0:
        print(f"Epoch {epoch} -- {loss.item():.6f}")
1
2
3
4
5
6
7
8
9
10
Epoch 0 -- 0.311211
Epoch 10 -- 0.178968
Epoch 20 -- 0.137678
Epoch 30 -- 0.117203
Epoch 40 -- 0.104800
Epoch 50 -- 0.096401
Epoch 60 -- 0.090298
Epoch 70 -- 0.085640
Epoch 80 -- 0.081956
Epoch 90 -- 0.078961
1
print(f"a: {a}, b: {b}")
1
a: tensor([-4.1591], requires_grad=True), b: tensor([2.2001], requires_grad=True)
1
2
3
4
5
6
7
8
db_x = np.linspace(-4, 8, 121)
db_y = a.detach().numpy() + b.detach().numpy() * db_x
db_y = 1 / (1 + np.exp(-db_y))
plt.plot(db_x, db_y)

c = np.where(a + b * train_X > 0.0, 1, 0)
plt.scatter(train_X, train_y, c=c)
plt.show()

png

This post is licensed under CC BY 4.0 by the author.

[PyTorch] Transform

[PyTorch] Hyperparameter

Comments powered by Disqus.