데이터세트
데이터세트(Dataset): 학습에 필요한 데이터 샘플을 정제하고 정답을 저장하는 기능을 제공하는 클래스.
초기화 메소드(__init__), 호출 메소드(__getitem__), 길이 반환 메소드(__len__)를 재정의해 활용한다.
# 데이터세트 클래스 기본형
class Dataset:
def __init__(self, data, *arg, **kwargs):
self.data = data
def __getitem__(self, index):
return tuple(data[index] for data in data.tensors)
def __len__(self):
return self.data[0].size()
- 초기화 메소드(__init__): 입력된 데이터의 전처리 과정을 수행하는 메소드. 새로운 인스턴스가 생성될 때, 학습에 사용될 데이터를 선언하고 학습에 필요한 형태로 변형한다.
- 호출 메소드(__getitem__): 입력된 색인 (index)에 해당하는 데이터 샘플을 불러오고 반환하는 메소드. 초기화 메소드에서 변형되거나 개선된 데이터를 가져오며, 데이터 샘플과 정답을 반환한다.
- 길이 반환 메소드(__len__): 학습에 사용된 전체 데이터세트의 개수를 반환하는 메소드.
데이터로더
데이터로더(DataLoader): 데이터세트에 저장된 데이터를 불러오는 클래스.
원활한 학습을 위해 배치 크기(batch_size), 데이터 순서 변경(shuffle), 데이터 로드 프로세스 수(num_workers) 등의 기능을 제공한다.
- 배치 크기(batch_size): 학습 데이터 개수가 많아 한 번의 에폭에서 모든 데이터를 메모리에 올릴 수 없을 때, 데이터를 나누는 파라미터. 1000개의 데이터 샘플이 데이터세트의 전체 길이일 때 배치 크기가 100이라면, 1번의 에폭을 진행하려면 10번의 배치를 거쳐야 한다.
- 데이터 순서 변경(shuffle): 모델이 데이터 간의 관계가 아닌 순서로 학습되는 것을 막기 위해, 데이터의 순서를 변경하는 파라미터.
- 데이터 로드 프로세스 수(num_workers): 데이터를 불러올 때 사용할 프로세스의 개수. 데이터 로드에 필요한 프로세수의 수를 늘리면 데이터를 불러오는 시간을 단축할 수 있다.
- 배치 제거(drop_last): 배치 크기에 맞지 않는 배치를 제거하는 파라미터. 예를 들어 전체 데이터세트의 크기가 8일 때, 배치 크기가 3이라면 마지막 배치의 크기는 2가 된다. drop_last의 값을 True로 지정하면, 크기가 2인 마지막 배치, 즉, 불완전한 배치를 학습에 포함시키지 않는다.

다중 선형 회귀
다중 선형 회귀를 구현하기 위해, 아래의 데이터를 사용한다.
| x1 | x2 | y1 | y2 |
|---|---|---|---|
| 1 | 2 | 0.1 | 1.5 |
| 2 | 3 | 1.0 | 2.8 |
| 3 | 4 | 1.9 | 4.1 |
| 4 | 5 | 2.8 | 5.4 |
| 5 | 6 | 3.7 | 6.7 |
| 6 | 7 | 4.6 | 8.0 |
이 표에서 $x_{1}$과 $x_{2}$는 독립변수, $y_{1}$과 $y_{2}$는 종속변수이며, 이를 다중 선형 회귀의 식으로 나타내면 다음과 같다.
$$ y_{1} = w_{1}x_{1} + w_{2}x_{2} + b_{1} \\ y_{2} = w_{3}x_{1} + w_{4}x_{2} + b_{2}$$
그리고 $y_{1}$과 $y_{2}$를 계산하기 위한 가중치와 편향 중 하나는 다음과 같다.
$$ y_{1} = 1.7x_{1} - 0.8x_{2} + 0 \\ y_{2} = 1.1x_{1} + 0.2x_{2} + 0$$
그리고 모델 매개변수(model.parameters)는 가중치와 편향을 다음과 같은 행렬의 형태로 반환한다.
$$ \begin{align*}
&Weight = \left[
\begin{matrix}
w_{1} & w_{2} \\
w_{3} & w_{4} \\
\end{matrix}
\right] = \left[
\begin{matrix}
1.7 & -0.8 \\
1.1 & 0.2 \\
\end{matrix}
\right] \\
&Bias = \left[
\begin{matrix}
b_{1} \\ b_{2}
\end{matrix}
\right] = \left[
\begin{matrix}
0 \\ 0
\end{matrix}
\right]
\end{align*} $$
이제 데이터세트와 데이터로더를 이용해 다중 선형 회귀를 구현해보겠다.
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader # 데이터세트와 데이터로더 임포트
# 데이터 선언
train_x = torch.FloatTensor([
[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]
])
train_y = torch.FloatTensor([
[0.1, 1.5], [1, 2.8], [1.9, 4.1], [2.8, 5.4], [3.7, 6.7], [4.6, 8]
])
텐서 데이터세트 (TensorDataset)를 이용해 훈련용 데이터세트를 생성한 뒤, 데이터로더로 이를 불러온다.
# 데이터세트와 데이터로더
train_dataset = TensorDataset(train_x, train_y)
train_dataloader = DataLoader(train_dataset, batch_size=2, shuffle=True, drop_last=True)
데이터로더까지 선언했다면, 모델, 오차 함수, 최적화 함수를 선언한다.
다중 선형 회귀도 Linear()를 사용하며, 학습 데이터로 사용하는 train_x와 train_y가 모두 (n, 2)의 크기를 가지므로,
Linear()의 입력 · 출력 데이터 차원 크기는 2가 된다.
# 모델, 오차 함수, 최적화 함수 선언
model = nn.Linear(2, 2, bias=True)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)
에폭마다 오차를 다시 계산해야 하기 때문에, 에폭이 시작될 때마다 cost를 0으로 초기화한다.
이전에 단순 선형 회귀를 구현했을 땐 전체 데이터세트를 불러와 학습을 진행했지만,
이번에는 배치 크기로 데이터를 학습하기 때문에 오차가 아닌 손실(loss)을 계산한다.
for문을 이용해 train_dataloader를 반복해 batch를 반환한다.
batch 변수에는 데이터세트에 입력한 순서로 데이터가 반환되어 train_x, train_y가 포함된다.
손실(loss) 값을 계산하고, 배치마다 오차(cost)에 이 값을 누적해서 더한 뒤, 이를 데이터로더의 길이만큼 나눠 오차의 평균값을 구한다.
for epoch in range(10000):
cost = 0
for batch in train_dataloader:
x, y = batch
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
cost += loss
cost /= len(train_dataloader)
if (epoch + 1) % 1000 == 0:
print(f"Epoch: {epoch + 1: 4d}, Model: {list(model.parameters())}, Cost: {cost: .3f}")
# 데이터세트와 데이터로더를 이용한 다중 선형 회귀 (전체 코드)
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
train_x = torch.FloatTensor([
[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]
])
train_y = torch.FloatTensor([
[0.1, 1.5], [1, 2.8], [1.9, 4.1], [2.8, 5.4], [3.7, 6.7], [4.6, 8]
])
train_dataset = TensorDataset(train_x, train_y)
train_dataloader = DataLoader(train_dataset, batch_size=2, shuffle=True, drop_last=True)
model = nn.Linear(2, 2, bias=True)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)
for epoch in range(10000):
cost = 0
for batch in train_dataloader:
x, y = batch
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
cost += loss
cost /= len(train_dataloader)
if (epoch + 1) % 1000 == 0:
print(f"Epoch: {epoch + 1: 4d}, Model: {list(model.parameters())}, Cost: {cost: .3f}")
Epoch: 1000, Model: [Parameter containing:
tensor([[ 0.9574, -0.1214],
[ 0.5215, 0.6882]], requires_grad=True), Parameter containing:
tensor([-0.4094, -0.1081], requires_grad=True)], Cost: 0.021
Epoch: 2000, Model: [Parameter containing:
tensor([[ 1.0224, -0.1549],
[ 0.6132, 0.6408]], requires_grad=True), Parameter containing:
tensor([-0.5079, -0.2472], requires_grad=True)], Cost: 0.005
Epoch: 3000, Model: [Parameter containing:
tensor([[ 1.0555, -0.1720],
[ 0.6599, 0.6167]], requires_grad=True), Parameter containing:
tensor([-0.5581, -0.3180], requires_grad=True)], Cost: 0.001
(생략)
Epoch: 9000, Model: [Parameter containing:
tensor([[ 1.0892, -0.1895],
[ 0.7076, 0.5920]], requires_grad=True), Parameter containing:
tensor([-0.6093, -0.3903], requires_grad=True)], Cost: 0.000
Epoch: 10000, Model: [Parameter containing:
tensor([[ 1.0895, -0.1896],
[ 0.7080, 0.5918]], requires_grad=True), Parameter containing:
tensor([-0.6097, -0.3909], requires_grad=True)], Cost: 0.000'ML · DL > Pytorch 공부' 카테고리의 다른 글
| [파이토치 기초] 데이터세트 분리 (0) | 2025.01.07 |
|---|---|
| [파이토치 기초] 모듈 (0) | 2025.01.04 |
| [파이토치 기초] 경사 하강법 (0) | 2024.12.26 |
| [파이토치 기초] 손실 함수 (0) | 2024.12.24 |
| [파이토치 기초] 텐서 (0) | 2024.12.16 |