๐ ์ค๋์ ๋ชฉํ: ์๋์ฐจ ์ฐ๋น ์์ธก ๋ชจ๋ธ ๋ง๋ค๊ธฐ
์ค๋์ 1970-80๋ ๋์ ํด๋์ํ ์๋์ฐจ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ฐ๋น(MPG)๋ฅผ ์์ธกํ๋ DNN ํ๊ท ๋ชจ๋ธ์ PyTorch๋ก ๋ง๋ค์ด๋ณด์๋ค. ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ๋ถํฐ ํน์ฑ ๊ณตํ, ๋ชจ๋ธ๋ง, ๊ทธ๋ฆฌ๊ณ ํ๊ฐ๊น์ง ์ ์ฒด ํ์ดํ๋ผ์ธ์ ๊ฒฝํํด๋ณด๋ ์์ฐฌ ์ค์ต์ด์๋ค. ๐
โจ ํต์ฌ ์ ๋ฆฌ
#๋ฐ์ดํฐ์ ์ฒ๋ฆฌ #ํน์ฑ๊ณตํ #์ ๊ทํ #PyTorch #DNN #ํ๊ท๋ชจ๋ธ #๊ท์
๐ ๏ธ ํ๋์ ๋ณด๋ ๊ณผ์
๋จ๊ณ | ๋ด์ฉ | ํต์ฌ ๊ธฐ์ /๊ฐ๋ |
---|---|---|
1. ๋ฐ์ดํฐ ์ค๋น | UCI ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ pandas ๋ก ํ์ธํ๋ค. |
pandas.read_csv |
2. ์ ์ฒ๋ฆฌ | Horsepower ์ด์ ๊ฒฐ์ธก์น๋ฅผ ๊น๋ํ๊ฒ ์ ๊ฑฐํ๋ค. |
dropna() |
3. ํน์ฑ ๊ณตํ | Model Year ๋ ๊ทธ๋ฃน์ผ๋ก ๋ฌถ๊ณ , Origin ์ ์-ํซ ์ธ์ฝ๋ฉ์ ์๋ํ๋ค. |
torch.bucketize , one_hot |
4. ์ ๊ทํ | StandardScaler ๋ก ์์น ๋ฐ์ดํฐ๋ค์ ์ค์ผ์ผ์ ๋ง์ท๋ค. ์ ํ๋ฉด ํ์ต์ด ๋ถ์์ ํด์ง๋ค. |
StandardScaler |
5. ๋ชจ๋ธ๋ง ๋ฐ ํ์ต | PyTorch๋ก ๊ฐ๋จํ DNN ํ๊ท ๋ชจ๋ธ์ ๋ง๋ค๊ณ ์ด์ฌํ ํ์ต์์ผฐ๋ค. ๐ค | torch.nn.Sequential , MSELoss , SGD |
6. ์ฑ๋ฅ ํ๊ฐ ๋ฐ ๊ท์ | ํ์ต๋ ๋ชจ๋ธ์ ์ฑ๋ฅ์ MSE์ MAE๋ก ํ๊ฐํ๊ณ , ๊ณผ์ ํฉ์ ๋ง๊ธฐ ์ํ L1/L2 ๊ท์ ๋ ์ดํด๋ณด์๋ค. | L1Loss , weight_decay |
๐ ์ฝ์ง๊ณผ ๋ฐฐ์์ ๊ธฐ๋ก
1. ๋ฐ์ดํฐ ์ค๋น ๋ฐ ์ ์ฒ๋ฆฌ
- ๋ฐ์ดํฐ ๋ก๋:
pd.read_csv
๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์๋ค. ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ ํ์ผ์ด๋ผsep=" "
์ต์ ์ ์คฌ๋ค. - ๊ฒฐ์ธก์น ์ฒ๋ฆฌ:
Horsepower
์ด์?
๊ฐ ์์ฌ์์๋ค.na_values='?'
๋กNaN
๋ณํ ํdropna()
๋ก ๋ ๋ ค๋ฒ๋ ธ๋ค. ์ ์์! ๐ - ๋ฐ์ดํฐ ๋ถํ :
train_test_split
์ผ๋ก ํ๋ จ์ฉ๊ณผ ํ ์คํธ์ฉ ๋ฐ์ดํฐ๋ฅผ ๋๋ด๋ค. ๊ธฐ๋ณธ ์ค์ ๊ธฐ๋ณธ์ด๋ค.
# ๋ฐ์ดํฐ ๋ก๋ ๋ฐ ์ ์ฒ๋ฆฌ
import pandas as pd
import torch
from sklearn.model_selection import train_test_split
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data'
column_names = ['MPG', 'Cylinders', 'Displacement', 'Horsepower', 'Weight',
'Acceleration', 'Model Year', 'Origin']
df = pd.read_csv(url, names=column_names, na_values="?", comment='\t', sep=" ", skipinitialspace=True)
df = df.dropna()
# ๋ฐ์ดํฐ ๋ถํ
X_train_df, X_test_df, y_train_df, y_test_df = train_test_split(df.iloc[:, 1:], df['MPG'], test_size=0.2, random_state=121)
2. ๋ฐ์ดํฐ ์ ๊ทํ (Normalization)
์ ๊ทํ๋ ์ ํ์ด ์๋ ํ์! โจ
ํน์ฑ๋ง๋ค ๊ฐ์ ๋ฒ์๊ฐ ๋๋ฌด ๋ฌ๋ผ์(Weight
vsCylinders
) ์ ๊ทํ๋ ํ์์๋ค. ์ ๊ทธ๋ฌ๋ฉด ๋ชจ๋ธ์ด ํฐ ๊ฐ์๋ง ํ๋๋ฆด ์ ์๋ค.StandardScaler
๋ฅผ ์จ์ ๊ฐ๋จํ ํด๊ฒฐํ๋ค.
from sklearn.preprocessing import StandardScaler
numeric_columns = ['Cylinders', 'Displacement', 'Horsepower', 'Weight', 'Acceleration']
scaler = StandardScaler()
# ํ๋ จ ๋ฐ์ดํฐ ๊ธฐ์ค์ผ๋ก ์ค์ผ์ผ๋ฌ ํ์ต ๋ฐ ์ ์ฉ
X_train_df[numeric_columns] = scaler.fit_transform(X_train_df[numeric_columns])
# ํ
์คํธ ๋ฐ์ดํฐ๋ ํ์ต๋ ์ค์ผ์ผ๋ฌ๋ก ๋ณํ๋ง!
X_test_df[numeric_columns] = scaler.transform(X_test_df[numeric_columns])
3. ํน์ฑ ๊ณตํ (Feature Engineering)
Model Year
: ์ฐ๋๋ฅผ ๋ช ๊ฐ ๊ทธ๋ฃน์ผ๋ก ๋ฌถ์๋ค (torch.bucketize
). ๋๋ฌด ๋ง์ ์ฐ๋๋ฅผ ๊ทธ๋๋ก ์ฐ๊ธฐ๋ณด๋ค ๊ทธ๋ฃน์ผ๋ก ๋ง๋๋ ๊ฒ ๋ซ๋ค๊ณ ํ๋จํ๋ค.Origin
: ์ ์กฐ ๊ตญ๊ฐ๋ฅผ ์-ํซ ์ธ์ฝ๋ฉ(one_hot
)ํ๋ค. ๋ชจ๋ธ์ด ๊ตญ๊ฐ๋ฅผ ์์ด๋ก ์คํดํ๋ฉด ์ ๋๋๊น!
from torch.nn.functional import one_hot
# Model Year ๋ฒํทํ
boundaries = torch.tensor([73, 76, 79])
X_train_df['Model Year Bucketed'] = torch.bucketize(torch.tensor(X_train_df['Model Year'].values), boundaries, right=True)
X_test_df['Model Year Bucketed'] = torch.bucketize(torch.tensor(X_test_df['Model Year'].values), boundaries, right=True)
# Origin ์-ํซ ์ธ์ฝ๋ฉ ๋ฐ ํ
์ ๊ฒฐํฉ
train_origin_encoded = one_hot(torch.tensor(X_train_df['Origin'].values) % 3)
test_origin_encoded = one_hot(torch.tensor(X_test_df['Origin'].values) % 3)
x_train = torch.cat([torch.tensor(X_train_df[numeric_columns + ['Model Year Bucketed']].values), train_origin_encoded], 1).float()
x_test = torch.cat([torch.tensor(X_test_df[numeric_columns + ['Model Year Bucketed']].values), test_origin_encoded], 1).float()
y_train = torch.FloatTensor(y_train_df.values)
y_test = torch.FloatTensor(y_test_df.values)
4. DNN ๋ชจ๋ธ ๊ตฌ์ถ ๋ฐ ํ์ต
- ๋ชจ๋ธ ์ค๊ณ:
torch.nn.Sequential
๋ก ๊ฐ๋จํ DNN ๋ชจ๋ธ์ ๋ง๋ค์๋ค. ์๋์ธต 2๊ฐ์ ํ์ฑํ ํจ์๋ ReLU! - ์์ค ํจ์์ ์ตํฐ๋ง์ด์ : ํ๊ท ๋ฌธ์ ๋ผ ์์ค ํจ์๋
MSELoss
, ์ตํฐ๋ง์ด์ ๋ ํด๋์ํSGD
๋ฅผ ์ผ๋ค. - ํ์ต ์์!:
DataLoader
๋ก ๋ฐ์ดํฐ๋ฅผ ์กฐ๊ธ์ฉ ๋ชจ๋ธ์ ์ฃผ๋ฉด์ 200 ์ํฌํฌ ๋์ ํ์ต์์ผฐ๋ค. ์์ค์ด ์ญ์ญ ๋จ์ด์ง๋ ๊ฑธ ๋ณด๋ ๋ฟ๋ฏํ๋ค. ๐
from torch.utils.data import DataLoader, TensorDataset
import torch.nn as nn
# ๋ฐ์ดํฐ ๋ก๋ ์์ฑ
train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=8, shuffle=True)
# ๋ชจ๋ธ ์ ์
hidden_units = [8, 4]
input_size = x_train.shape[1]
all_layers = []
for hidden_unit in hidden_units:
layer = nn.Linear(input_size, hidden_unit)
all_layers.append(layer)
all_layers.append(nn.ReLU())
input_size = hidden_unit
all_layers.append(nn.Linear(hidden_units[-1], 1))
model = nn.Sequential(*all_layers)
# ์์ค ํจ์ ๋ฐ ์ตํฐ๋ง์ด์
loss_fn = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
# ๋ชจ๋ธ ํ์ต
torch.manual_seed(1)
num_epochs = 200
for epoch in range(num_epochs):
loss_train = 0
for X, y in train_dl:
pred = model(X).squeeze()
loss = loss_fn(pred, y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
loss_train += loss.item()
if epoch % 20 == 0:
print(f'Epoch {epoch}, Loss {loss_train/len(train_dl):.4f}')
5. ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ ๋ฐ ๊ท์
- ์ฑ๋ฅ ํ๊ฐ: ํ์ต์ด ๋๋ ๋ชจ๋ธ์ ํ ์คํธ ๋ฐ์ดํฐ๋ก ํ๊ฐํ๋ค. MSE์ MAE ๊ฐ์ ํ์ธํ๋, ๋์์ง ์์ ๊ฒฐ๊ณผ๊ฐ ๋์๋ค.
- ๊ณผ์ ํฉ ๋ฐฉ์ง: ๋ชจ๋ธ์ด ํ๋ จ ๋ฐ์ดํฐ์๋ง ๋๋ฌด ์ต์ํด์ง๋ ๊ฑธ ๋ง๊ธฐ ์ํด ๊ท์ (Regularization)๋ผ๋ ๊ธฐ๋ฒ์ด ์๋ค.
- L1 (Lasso): ๋ถํ์ํ ํน์ฑ์ ๊ฐ์ค์น๋ฅผ 0์ผ๋ก ๋ง๋ค์ด ๋ฒ๋ฆฐ๋ค.
- L2 (Ridge): ๊ฐ์ค์น๋ค์ด ๋๋ฌด ์ปค์ง์ง ์๊ฒ ์ ๋ฐ์ ์ผ๋ก ๋๋ฅด๋ ๋๋.
SGD
์ตํฐ๋ง์ด์ ์weight_decay
๋ก ์ฝ๊ฒ ์ ์ฉํ ์ ์๋ค.
# ๋ชจ๋ธ ํ๊ฐ
model.eval()
with torch.no_grad():
pred = model(x_test).squeeze()
test_mse = loss_fn(pred, y_test)
test_mae = nn.L1Loss()(pred, y_test)
print(f'Test MSE: {test_mse.item():.4f}') # ๊ฒฐ๊ณผ ํ์ธ!
print(f'Test MAE: {test_mae.item():.4f}')
# L2 ๊ท์ ์ ์ฉ ์์
optimizer_l2 = torch.optim.SGD(model.parameters(), lr=0.001, weight_decay=0.5)
โจ ์ค๋์ ํ๊ณ
๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ๋ถํฐ ๋ชจ๋ธ๋ง, ํ๊ฐ๊น์ง ์ ์ฒด ํ์ดํ๋ผ์ธ์ ๊ฒฝํํด๋ณธ ์ข์ ์ค์ต์ด์๋ค. ํนํ ๋ฐ์ดํฐ ์ ๊ทํ์ ์ค์์ฑ์ ๋ค์ ํ๋ฒ ๋๊ผ๋ค. PyTorch๋ก ์ง์ ๋ชจ๋ธ์ ์ง๋ณด๋ ๋ฅ๋ฌ๋ ๋ชจ๋ธ์ ๊ตฌ์กฐ๊ฐ ๋ ๋ช ํํ๊ฒ ์ดํด๋๊ณ , ํ๋ จ ์์ค๊ณผ ํ ์คํธ ์์ค์ ๋น๊ตํ๋ฉฐ ๋ชจ๋ธ์ ์ผ๋ฐํ ์ฑ๋ฅ์ ๊ฐ๋ ํด๋ณผ ์ ์์๊ณ , ๊ท์ ์ ํ์์ฑ๋ ์ฒด๊ฐํ๋ค.
๋ค์์๋ ํ์ดํผํ๋ผ๋ฏธํฐ ํ๋์ผ๋ก ๋ชจ๋ธ ์ฑ๋ฅ์ ๋ ๋์ด์ฌ๋ ค ๋ด์ผ๊ฒ ๋ค.