모듈
파이토치의 모델(Model)은 인공 신경망 모듈 클래스를 활용해 복잡한 구조의 신경망을 효율적으로 구축하고 관리할 수 있게 해준다.
모델 구현은 신경망 패키지의 모듈(Module) 클래스를 활용하며, 새로운 모델은 이를 상속받아 서브 클래스로 생성한다.
이 클래스는 다른 모듈을 포함하거나 트리 구조로 중첩할 수 있다.
모듈 클래스는 초기화 메소드(__init__)와 순방향 메소드(forward)를 재정의해 활용한다.
초기화 메소드에서는 신경망에 사용될 레이어를 초기화하고, 순방향 메소드에서는 모델이 어떤 구조를 갖게 될지 정의한다. 모듈 클래스를 통해 모델을 정의해 모델 객체를 호출하는 순간, 순방향 메소드가 실행된다.
# 모듈 클래스 기본형
class Model(nn.Module):
def __init__(self):
super().__init()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv1 = nn.Conv2d(20, 20, 5)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
return x
초기화 메소드(__init__)는 신경망 레이어를 정의하기 전에 super() 함수를 사용해 부모 클래스의 속성을 초기화한다. 이를 통해 서브 클래스인 모델에서 부모 클래스의 속성을 사용할 수 있다.
모델 클래스를 초기화한 후, 학습에 사용되는 레이어를 초기화 메소드에 정의하며, 위 코드의 self.conv1이나 self.conv2와 같은 인스턴스는 모델의 파라미터가 된다.
순방향 메소드(forward)는 초기화 메소드에서 선언한 모델 파라미터를 사용해 신경망 구조를 정의한다. 이 메소드는 모델이 데이터(x)를 입력받아 학습을 진행하는 과정을 구현한다.
모듈 클래스는 호출 가능한 형식(Callable Type)으로 인스턴스를 호출할 때 호출 메소드(__call__)가 순방향 메소드를 실행하므로, 모델 객체를 호출하면 순서대로 학습이 진행된다.
초기화 메소드에서 super 함수로 부모 클래스를 초기화했기 때문에 역방향 연산은 정의하지 않아도 된다.
파이토치의 자동 미분 기능인 Autograd에서 모델의 파라미터를 역으로 전파해 자동으로 기울기 또는 변화도를 계산해 주기 때문이다.
비선형 회귀
이번에는 FloatTensor에 직접 값을 기입하지 않고, csv 파일을 데이터로 사용한다.
필자가 사용하는 데이터는 다음과 같은 형태이다. x 데이터와 y 데이터는 이차 방정식의 관계를 갖는다.
| x | y |
|---|---|
| -10.0 | 327.79 |
| -9.9 | 321.39 |
| -9.8 | 314.48 |
| -9.7 | 308.51 |
| ... | ... |
# 라이브러리 및 프레임워크 선언
import torch
import pandas as pd # csv를 불러오기 위해 사용하는 라이브러리
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
# 사용자 정의 데이터세트
class CustomDataset(Dataset):
def __init__(self, file_path):
df = pd.read_csv(file_path)
print(df.head())
self.x = df.iloc[:, 0].values
self.y = df.iloc[:, 1].values
self.length = len(df)
def __getitem__(self, index):
x = torch.FloatTensor([self.x[index] ** 2, self.x[index]])
y = torch.FloatTensor([self.y[index]])
return x, y
def __len__(self):
return self.length
csv 파일을 데이터로 사용하기 위해 데이터세트 클래스를 상속받아 사용자 정의 데이터세트(CustomDataset)를 정의한다.
초기화 메소드(__init__)에서는 csv 파일 경로를 입력받을 수 있도록 file_path를 정의하고, csv 파일을 제대로 불러왔는지 확인하기 위해 head()를 이용해 csv의 첫 5줄을 출력한다.
pandas 라이브러리를 사용해 csv 파일을 불러온 후 x 값, y 값, 그리고 데이터 전체 길이를 저장한다.
호출 메소드(__getitem__)에서 x 값과 y 값을 반환한다.
이때 결과는 이차방정식 형태인 $(y = W_1x^2 + W_2x + b)$ 에 맞추어, x 값은 $[x^2, x]$의 구조로, y 값은 $[y]$ 구조로 반환한다.
길이 반환 메소드(__len__)로 초기화 메소드에서 선언한 self.length를 반환해 데이터의 길이를 제공한다.
# 사용자 정의 모델
class CustomModel(nn.Module):
def __init__(self):
super().__init__()
self.layer = nn.Linear(2, 1)
def forward(self, x):
x = self.layer(x)
return x
사용자 정의 데이터세트를 선언한 후, 모듈 클래스를 상속받아 사용자 정의 모델을 선언한다.
super()를 이용해 모듈 클래스의 속성을 초기화하고, 모델에서 사용할 레이어를 정의한다.
결과가 이차방정식 형태이므로, nn.Linear의 입력 데이터 차원은 2, 출력 데이터 차원은 1로 설정된다.
모델의 모든 파라미터를 선언한 후, 순방향 메소드에서는 학습 과정을 정의한다.
현재 모델의 레이어는 하나만 있으므로, self.layer에 입력 데이터 x를 전달하고 그 결과를 반환한다.
# 사용자 정의 데이터세트, 데이터로더
train_dataset = CustomDataset("file_path")
train_dataloader = DataLoader(train_dataset, batch_size=128, shuffle=True, drop_last=True)
사용자 정의 클래스를 모두 선언했다면, 이를 활용해 train_dataset, train_dataloader 인스턴스를 생성해 데이터를 사용할 준비를 한다.
# GPU 연산 적용
device = "cuda" if torch.cuda.is_available() else "cpu"
print(device) # GPU가 정상적으로 적용된다면 cuda가 출력됨. GPU가 없다면 cpu를 출력
model = CustomModel().to(device)
criterion = nn.MSELoss().to(device)
optimizer = optim.SGD(model.parameters(), lr=1e-4)
이후 모델, 오차 함수, 최적화 함수를 선언하고, 사용할 장치를 정의한다.
device 변수에 어떤 장치를 사용할지 삼항 연산자를 통해 정의한 뒤, to 메소드를 이용해 모델과 오차 함수 이를 적용한다.
# 학습 진행
for epoch in range(10000):
cost = 0.0
for x, y in train_dataloader:
x = x.to(device)
y = y.to(device)
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
cost += loss
cost = cost / len(train_dataloader)
if (epoch + 1) % 1000 == 0:
print(f"Epoch: {epoch + 1: 4d}, Model: {list(model.parameters())}, Cost: {cost: .3f}")
사용할 모델, 함수, 장치 정의가 끝났다면, 학습 코드를 구현한다.
이전에 사용했던 다중 선형 회귀 코드와 동일한 구조이나, GPU 연산을 적용하므로 학습에 사용되는 변수에 to 메소드를 적용하는 차이가 있다.
# 전체 코드
import torch
import pandas as pd
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
def __init__(self, file_path):
df = pd.read_csv(file_path)
print(df.head())
self.x = df.iloc[:, 0].values
self.y = df.iloc[:, 1].values
self.length = len(df)
def __getitem__(self, index):
x = torch.FloatTensor([self.x[index] ** 2, self.x[index]])
y = torch.FloatTensor([self.y[index]])
return x, y
def __len__(self):
return self.length
class CustomModel(nn.Module):
def __init__(self):
super().__init__()
self.layer = nn.Linear(2, 1)
def forward(self, x):
x = self.layer(x)
return x
train_dataset = CustomDataset("datasets/non_linear.csv")
train_dataloader = DataLoader(train_dataset, batch_size=128, shuffle=True, drop_last=True)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(device)
model = CustomModel().to(device)
criterion = nn.MSELoss().to(device)
optimizer = optim.SGD(model.parameters(), lr=1e-4)
for epoch in range(10000):
cost = 0.0
for x, y in train_dataloader:
x = x.to(device)
y = y.to(device)
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
cost += loss
cost = cost / len(train_dataloader)
if (epoch + 1) % 1000 == 0:
print(f"Epoch: {epoch + 1: 4d}, Model: {list(model.parameters())}, Cost: {cost: .3f}")
with torch.no_grad():
model.eval()
inputs = torch.FloatTensor(
[
[1 ** 2, 1],
[5 ** 2, 5],
[11 ** 2, 11]
]
).to(device)
outputs = model(inputs)
print(outputs)
x y
0 -10.0 327.79
1 -9.9 321.39
2 -9.8 314.48
3 -9.7 308.51
4 -9.6 302.86
cuda
Epoch: 1000, Model: [Parameter containing:
tensor([[ 3.1071, -1.7005]], device='cuda:0', requires_grad=True), Parameter containing:
tensor([0.0654], device='cuda:0', requires_grad=True)], Cost: 0.157
Epoch: 2000, Model: [Parameter containing:
tensor([[ 3.1066, -1.7030]], device='cuda:0', requires_grad=True), Parameter containing:
tensor([0.1017], device='cuda:0', requires_grad=True)], Cost: 0.140
Epoch: 3000, Model: [Parameter containing:
(생략)
Epoch: 9000, Model: [Parameter containing:
tensor([[ 3.1035, -1.7030]], device='cuda:0', requires_grad=True), Parameter containing:
tensor([0.2835], device='cuda:0', requires_grad=True)], Cost: 0.102
Epoch: 10000, Model: [Parameter containing:
tensor([[ 3.1035, -1.7028]], device='cuda:0', requires_grad=True), Parameter containing:
tensor([0.3015], device='cuda:0', requires_grad=True)], Cost: 0.091
tensor([[ 1.7022],
[ 69.3758],
[357.0981]], device='cuda:0')'ML · DL > Pytorch 공부' 카테고리의 다른 글
| [파이토치 기초] 모델 저장/불러오기 (0) | 2025.01.09 |
|---|---|
| [파이토치 기초] 데이터세트 분리 (0) | 2025.01.07 |
| [파이토치 기초] 데이터세트 & 데이터로더 (0) | 2024.12.30 |
| [파이토치 기초] 경사 하강법 (0) | 2024.12.26 |
| [파이토치 기초] 손실 함수 (0) | 2024.12.24 |