250923 [ Day 56 ] - PyTorch (4)

TaeHyun·2025년 9월 23일

TIL

목록 보기
58/184

시작하며

오늘부로 AI 파트가 모두 끝났다. 어제에 이어서 여러 모델들을 구현해보았다. 단일 선형 회귀처럼 간단한 모델은 상대적으로 쉬워서 이해하면서 코드 구현을 했었는데, 복잡한 모델을 구현하니까 이해는 둘째 치고 코드를 따라 치는 것도 벅찼던 것 같다. 코드를 계속 보면서 조금씩 이해해보려 해야겠다.

선형회귀모델

  • nn.Sequential() : 복잡한 모델 구현을 간단하게 표현 가능
class LinearRegression(nn.Module):
    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
            nn.Linear(1,10),
            nn.ReLU(),
            nn.Linear(10,10),
            nn.ReLU(),
            nn.Linear(10,1)
        )

    def forward(self, x):
        return self.model(x)

  • 트레이닝 / 테스트 데이터 분리
x = torch.linspace(0,10,100).view(-1,1)
y = 3 * x + 2 + torch.randn(100, 1) * 2

# Train / Test set 분리 (8:2로 분리)
num_train = int(len(x) * 0.8)
indices = torch.randperm(len(x))
train_idx, test_idx = indices[:num_train], indices[num_train:]

x_train, y_train = x[train_idx], y[train_idx]
x_test, y_test = x[test_idx], y[test_idx]

class LinearRegressionModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(1,1)

    def forward(self, x):
        return self.linear(x)
    
model = LinearRegressionModel()

criterion = nn.MSELoss() 
optimizer = optim.SGD(model.parameters(), lr=0.01)

epochs = 500
losses = []

# 트레이닝 데이터로 학습
for epoch in range(epochs):
    optimizer.zero_grad() 
    outputs = model(x_train) 
    loss = criterion(outputs, y_train)
    loss.backward()
    optimizer.step()

    losses.append(loss.item())

    if (epoch + 1) % 100 == 0:
        print(f"Eporch [{epoch+1} / {epochs}], Loss : {loss.item() : .4f}")

# 테스트 데이터 평가
with torch.no_grad():
    test_output = model(x_test)
    test_loss = criterion(test_output, y_test)

print(f"최종 Test Loss : {test_loss.item(): .4f}")

plt.figure(figsize=(10,5))

plt.subplot(1, 2, 1)
plt.plot(losses)
plt.title("Loss over Epochs")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid()

plt.subplot(1, 2, 2)
plt.scatter(x_train.numpy(), y_train.numpy(), label="Training Data")
plt.scatter(x_test.numpy(), y_test.numpy(), label="Test Data", color="g")
plt.plot(x.numpy(), model(x).detach().numpy(), color="r", label="Fitted Line")
plt.title("Linear Regression Fit")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()

plt.show()

  • 다중 선형 회귀 구현
# 데이터 정의 (10개 샘플)
X = torch.tensor([[50.0, 25.0, 30.0],
                [10.0, 20.0, 25.0],
                [5.0, 22.0, 30.0],
                [30.0, 26.0, 28.0],
                [40.0, 15.0, 28.0],
                [60.0, 35.0, 40.0],
                [70.0, 30.0, 35.0],
                [20.0, 15.0, 20.0],
                [25.0, 18.0, 22.0],
                [45.0, 28.0, 30.0]], dtype=torch.float32)

y = torch.tensor([[22.1], [10.4], [9.3], [18.5], [12.9], 
                [25.0], [27.5], [13.0], [15.0], [20.5]], dtype=torch.float32)

# 모델 정의
class MultipleLinearRegression(nn.Module):
    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
            nn.Linear(3, 1)
        )

    def forward(self, X):
        return self.model(X)
    
model = MultipleLinearRegression()

# 손실함수 정의
criterion = nn.MSELoss()

# 가중치 업데이트
optimizer = optim.SGD(model.parameters(), lr=0.0001)
epochs = 1000
losses = []

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(X)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

    losses.append(loss.item())

    if (epoch + 1) % 100 == 0:
        print(f"Epoch [{epoch+1} / {epochs}], Loss : {loss.item(): .4f}")

# 결과 시각화
plt.figure(figsize=(10,5))

# 손실 감소 그래프
plt.subplot(1, 2, 1)
plt.plot(losses)
plt.title("Training Loss over Epochs")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid()

# 데이터와 예측 결과
predicted = model(X).detach().numpy()
actual = y.numpy()

plt.subplot(1, 2, 2)
plt.scatter(range(len(actual)), actual, color="b", label="Actual")
plt.scatter(range(len(predicted)), predicted, color="r", label="Predicted")
plt.grid()

plt.title("Actual vs Predicted Sales")
plt.xlabel("Data index")
plt.ylabel("Sales")
plt.legend()

plt.show()

# Weight / Bias 값 출력
# 방법 1
weight = model.model[-1].weight.data
bias = model.model[-1].bias.data
print("\n학습된 모델의 파라미터")
print(f"Weight : {weight}")
print(f"Bias : {bias}")

# 방법 2
model_layer_idx = len(model.model) - 1

params = dict(model.named_parameters())
w = params[f"model.{model_layer_idx}.weight"].data
b = params[f"model.{model_layer_idx}.bias"].data
print(w)
print(b)

분류

로지스틱 회귀 - 이진 분류

  • from torch.autograd import Variable
  1. 데이터 입출력 정의
    • multivariate_normal(평균, 공분산, 점의 수)
np.random.seed(42) # 시드 고정
num_samples = 1000

# 평균 및 공분산 설정
mean_1 = np.array([1., 1.])
cov_1 = np.array([[1,0], [0,1]])
mean_2 = np.array([-1., -1.])
cov_2 = np.array([[1,0], [0,1]])

# 데이터 생성
data_1 = np.random.multivariate_normal(mean_1, cov_1, num_samples // 2)
data_2 = np.random.multivariate_normal(mean_2, cov_2, num_samples // 2)

# 데이터 확인
plt.scatter(data_1[:, 0], data_1[:, 1], color="b", label="Class1")
plt.scatter(data_2[:, 0], data_2[:, 1], color="r", label="Class0")
plt.legend()
plt.show()

# 데이터 정의 및 텐서로 변환
data = np.vstack((data_1, data_2))
labels = np.ones(num_samples)
labels[num_samples // 2:] = 0

data = torch.from_numpy(data).float()
labels = torch.from_numpy(labels).float()
labels = labels.view(-1, 1)
num_samples, num_features = data.shape

  1. 모델 정의
class LogisticRegression(nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.linear = nn.Linear(input_dim, output_dim)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        out = self.linear(x)
        out = self.sigmoid(out)
        return out

model = LogisticRegression(2, 1)

  1. 손실함수 정의(BCE)
criterion = nn.BCELoss()

  1. 가중치 업데이트(학습)
optimizer = optim.SGD(model.parameters(), lr=0.001)
epochs = 1000
losses = []

for epoch in range(epochs):
    inputs = Variable(data)
    targets = Variable(labels)

    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs, targets)
    loss.backward()
    optimizer.step()

    losses.append(loss.item())

    if (epoch + 1) % 100 == 0:
        print(f"Epoch [{epoch+1} / {epochs}], Loss : {loss.item(): .4f}")

  1. 시각화
plt.plot(losses)
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid()
plt.show()

# 결정 경계 그리기
w = model.linear.weight.data.numpy()
b = model.linear.bias.data.numpy()
x_plot = np.array([-2,2])
y_plot = (-b - w[0][0] * x_plot / w[0][1]) # 결정 경계 함수
plt.plot(x_plot, y_plot, color="g", label="Decision Boundary")
plt.scatter(data_1[:, 0], data_1[:, 1], color="b", label="Class1")
plt.scatter(data_2[:, 0], data_2[:, 1], color="r", label="Class0")
plt.legend()
plt.show()

FashionMNIST 신경망

다항 분류

from torch.utils.data import DataLoader
from torchvision import datasets, transforms

  1. 데이터 입출력 정의
transform = transforms.ToTensor() # 이미지 데이터를 Tensor로 변환

# 트레이닝 테이터 정의
train_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=transform
)

# 테스트 데이터 정의
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=transform
)

# 데이터 로더 정의
batch_size = 64 # 미니 배치 경사 하강법
train_dataloader = DataLoader(train_data, batch_size=batch_size, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=batch_size, shuffle=False)

  1. 모델 정의
class MultiClassificationModeel(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.model = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.BatchNorm1d(512),
            nn.ReLU(),
            nn.Linear(512, 256),
            nn.BatchNorm1d(256),
            nn.ReLU(),
            nn.Linear(256, 64),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.flatten(x)
        out = self.model(x)
        return out

model = MultiClassificationModeel()

  1. 손실함수 정의
criterion = nn.CrossEntropyLoss()

  1. 가중치 업데이트
optimizer = optim.SGD(model.parameters(), lr=0.001)
epochs = 10

# 학습 루프
def train_loop(dataloader, model, criterion, optimizer):
    model.train() # 학습 모드
    size = len(dataloader.dataset)
    running_loss = 0.

    # x = 데이터 y = 정답 batch = idx
    for batch, (x, y) in enumerate(dataloader):
        optimizer.zero_grad() # 초기화
        outputs = model(x) # 순전파
        loss = criterion(outputs, y) # 손실 계산
        loss.backward() # 역전파
        optimizer.step() # 가중치 업데이트

        running_loss += loss.item() * x.size(0) # loss에 batch size를 곱함

        if (batch+1) % 100 == 0:
            current = batch * len(x)
            print(f"[batch : {batch+1: 4d}], Loss : {loss.item():>7f} ({current:>5d} / {size:>5d})")
    
    epoch_loss = running_loss / size
    return epoch_loss

# 테스트 루프
def test_loop(dataloader, model, criterion):
    model.eval() # 평가 모드
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss = 0.
    correct = 0

    with torch.no_grad():
        for x, y in dataloader:
            outputs = model(x)
            loss = criterion(outputs, y)
            test_loss += loss.item()
            correct += (outputs.argmax(1) == y).sum().item()

    avg_loss = test_loss / num_batches
    accuracy = correct / size
    print(f"Test - Accuracy : {100*accuracy:>5.1f}%, Avg_loss : {avg_loss:>8f}")
    return avg_loss, accuracy

# 학습 실행
for epoch in range(epochs):
    print(f"\n[Epoch] {epoch+1} / {epochs}")
    train_loss = train_loop(train_dataloader, model, criterion, optimizer)
    val_loss, val_acc = test_loop(test_dataloader, model, criterion)

print("\n완료!")

  1. 시각화
label_tags = {
    0: "T-Shirt", 1: "Trouser", 2: "Pullover", 3: "Dress", 4: "Coat", 5: "Sandal", 6: "Shirt", 7: "Sneaker", 8: "Bag", 9: "Ankle Boot"
}

rows, columns = 6, 6
fig = plt.figure(figsize=(15,15))
model.eval()

for i in range(1, rows * columns + 1):
    data_idx = np.random.randint(len(test_data))
    img_tensor, trus_label = test_data[data_idx]

    with torch.no_grad():
        x = img_tensor.unsqueeze(0) # 데이터 변형
        output = model(x)
        pred_idx = output.argmax(1).item()

    pred_class = label_tags[pred_idx] # 예측한 태그
    true_class = label_tags[trus_label] # 정답 태그

    is_correct = (pred_idx == int(trus_label))
    title = f"{pred_class}, Correct!" if is_correct else f"{pred_class}, Incorrect! answer : {true_class}"
    cmap = "Blues" if is_correct else "Reds"

    ax = fig.add_subplot(rows, columns, i)
    ax.imshow(img_tensor.squeeze(0).numpy(), cmap=cmap)
    ax.set_title(title, fontsize=10)
    ax.axis("off")

plt.tight_layout()
plt.show()

마치며

내일부터는 새로운 프로젝트를 시작하는데, 그동안 배웠던 OpenCV와 AI 기능을 활용한 프로젝트인 것 같은데 어떤 주제일지 전혀 감이 안 잡힌다. 최근에 배웠던 내용들이 꽤 까다로운 내용들이 많아서 살짝 걱정이 앞서는 것 같다.

NOTION

MY NOTION (Models. 01)

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글