경희대학교 수업 프로젝트로 진행했던
전체 코드는 다음과 같다.
1. 필요한 라이브러리 불러오기
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset, ConcatDataset
import glob
from tqdm import tqdm
from LSTMModel import LSTMModel
코드의 첫 번째 단계는 데이터 처리와 모델 학습에 필요한 라이브러리를 불러오는 것이다. numpy와 pandas는 데이터를 다루고 전처리하는 데 사용되며 sklearn.model_selection의 train_test_split 함수는 데이터를 훈련 데이터와 검증 데이터로 나누는 데 사용된다. PyTorch 관련 라이브러리인 torch와 torch.nn은 신경망 모델을 구성하고 학습시키는 데 필수적이며, DataLoader와 TensorDataset은 데이터를 배치 단위로 로드하는 데 사용된다. glob는 데이터 파일을 찾는 데 사용되고, tqdm은 학습 진행 상황을 시각적으로 보여준다. 마지막으로, LSTMModel은 사용자가 정의한 LSTM 모델을 불러오는 코드이다.
2. 학습 환경 준비
# Hyperparameters
TIME_STEP = 5
STRIDE = 1
INPUT_SIZE = 24
OUTPUT_SIZE = 1
BATCH_SIZE = 161
HIDDEN_LAYER_SIZE = 100
EPOCHS = 50
LEARNNG_RATE = 0.0005
# path and filename to save trained model
OUTPUT_FILENAME = 'model/free_1.pth'
# path to dataset
DATA_PATH = 'data'
# selecting columns from dataset csv file
x_features = [i for i in range(24)]
y_features = [48]
csv_file_paths = glob.glob(f'{DATA_PATH}/**/*.csv', recursive=True)
for path in csv_file_paths:
print(path)
cuda_available = torch.cuda.is_available()
print(cuda_available)
데이터셋을 모델에 학습하기 이전에 IMU 트래킹 데이터와 VR 핸드 트래킹 데이터를 사용하여 모델을 학습하는 과정에서 필요한 주요 하이퍼파라미터를 설정하고, 데이터셋을 로드하며, 모델을 학습할 수 있는 환경을 준비하는 것이 필요하다.
먼저, 코드에서는 하이퍼파라미터를 설정한다. TIME_STEP = 5는 LSTM 모델에서 한 번에 사용하는 시점의 크기를 정의하며, 여기서는 5개의 연속적인 데이터를 사용해 예측을 한다. STRIDE = 1은 슬라이딩 윈도우 방식으로 데이터를 이동시키는 보폭을 설정합니다. INPUT_SIZE = 24는 IMU 센서에서 측정되는 데이터의 특성값 개수를 의미하며, 예를 들어 3개의 가속도 센서와 3개의 자이로스코프 값 등이 포함될 수 있다. OUTPUT_SIZE = 1은 예측하고자 하는 출력값의 크기이다. 이 경우 손의 위치나 동작을 예측하기 위해 출력값은 1개의 값으로 설정된다. BATCH_SIZE = 161은 학습 시 한 번에 모델에 공급되는 데이터 샘플의 수를 정의한다. HIDDEN_LAYER_SIZE = 100은 LSTM 모델의 숨겨진 상태 크기를 설정하며, EPOCHS = 50은 학습을 반복할 에포크의 수이다. 마지막으로 LEARNNG_RATE = 0.0005는 학습률을 설정하여 모델의 가중치를 업데이트하는 크기를 조정한다.
그 다음에는 모델의 학습 결과를 저장할 경로를 설정한다. OUTPUT_FILENAME = 'model/free_1.pth'는 학습이 완료된 모델의 가중치를 저장할 경로를 지정한다. pth 파일은 PyTorch에서 모델을 저장하고 불러오는 표준 형식으로, 이 파일을 통해 나중에 모델을 로드하고 예측을 할 수 있다.
데이터셋 로드 과정에서는 glob.glob(f'{DATA_PATH}/**/*.csv', recursive=True)를 사용하여 지정된 경로에서 모든 .csv 파일을 찾는다. DATA_PATH는 데이터가 저장된 폴더 경로로, 이 코드에서는 IMU 트래킹 데이터와 VR 핸드 트래킹 데이터 파일을 로드하는 역할을 한다. 이 코드가 실행되면 해당 폴더 내의 모든 .csv 파일 경로가 출력된다.
다음으로, GPU 사용 여부를 확인한다. torch.cuda.is_available() 함수는 현재 환경에서 GPU를 사용할 수 있는지 여부를 확인하는 코드이다. GPU가 사용 가능하면 학습 속도가 빠르게 진행될 수 있기 때문에, 이 값을 통해 GPU를 사용할 수 있는지 판단하고, 이를 기반으로 모델과 데이터를 GPU로 이동시킬지 여부를 결정한다. cuda_available 변수는 True 또는 False 값을 반환하여 GPU 사용 가능 여부를 알려준다.
마지막으로, 특성 열(x_features)과 라벨 열(y_features)을 설정한다. x_features는 IMU 데이터에서 사용할 입력값의 열 인덱스를 정의하며, 이 코드에서는 24개의 특성값을 사용하고 있다. y_features는 VR 핸드 트래킹 데이터에서 예측하려는 출력값의 열 인덱스를 설정하며, 여기서는 48번째 열을 예측하려고 합니다. 이 과정은 데이터셋을 모델에 맞게 준비하는 중요한 부분이다.
이렇게 하이퍼파라미터와 데이터 경로, GPU 사용 여부를 확인한 후, 본격적인 모델 학습을 시작할 준비를 완료한다.
3. 파일 입력 및 데이터 전처리
def preprocess_file(file_path, time_steps):
data = pd.read_csv(file_path)
X_features = data.values[:, x_features]
y_target = data.values[:, y_features]
X, y = create_dataset(X_features, y_target, time_steps)
return X, y
def create_dataset(X, y, time_steps, stride=STRIDE):
Xs, ys = [], []
for i in range(0, len(X) - time_steps, stride): # i의 증가량을 stride로 조정 -> sequence에서 시작 index를 stride 숫자씩 옮겨가며 시작 index ~ index + time_step만큼의 열을 추출
v = X[i:(i + time_steps)] # input feature data sequence
Xs.append(v)
ys.append(y[i + time_steps])
return np.array(Xs), np.array(ys)
def load_and_process_data(file_paths, time_steps, batch_size=16):
all_X, all_y = [], []
for file_path in file_paths:
# 파일별 데이터 처리
X, y = preprocess_file(file_path, time_steps)
all_X.append(X)
all_y.append(y)
# 모든 데이터를 하나로 합침
combined_X = np.concatenate(all_X, axis=0)
combined_y = np.concatenate(all_y, axis=0)
# 데이터 분할
X_train, X_val, y_train, y_val = train_test_split(
combined_X, combined_y, test_size=0.1, random_state=42)
# DataLoader 생성
train_loader = DataLoader(TensorDataset(
torch.tensor(X_train, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.float32)),
batch_size=batch_size,
shuffle=True)
val_loader = DataLoader(TensorDataset(
torch.tensor(X_val, dtype=torch.float32),
torch.tensor(y_val, dtype=torch.float32)),
batch_size=batch_size,
shuffle=False)
return train_loader, val_loader
def create_final_loaders(csv_file_paths, time_steps, batch_size):
# csv_file_paths 분할
subsets = [csv_file_paths[x:x+100] for x in range(0, len(csv_file_paths), 100)]
# 각 서브셋에 대한 DataLoader 리스트 초기화
train_loaders = []
valid_loaders = []
# 각 서브셋에 대해 DataLoader 생성 및 리스트에 추가
for subset in subsets:
train_loader, valid_loader = load_and_process_data(
subset,
time_steps=time_steps,
batch_size=batch_size)
train_loaders.append(train_loader)
valid_loaders.append(valid_loader)
# DataLoader들을 합친 최종 DataLoader 생성
final_train_loader = DataLoader(ConcatDataset(
[loader.dataset for loader in train_loaders]),
batch_size=batch_size,
shuffle=True)
final_valid_loader = DataLoader(ConcatDataset(
[loader.dataset for loader in valid_loaders]),
batch_size=batch_size,
shuffle=False)
return final_train_loader, final_valid_loader
trainloader, validloader = create_final_loaders(
csv_file_paths, time_steps=TIME_STEP, batch_size=BATCH_SIZE)
이 코드는 IMU와 VR 손 추적 데이터가 여러 개의 CSV 파일로 저장되어 있을 때, 이를 LSTM 모델 학습에 적합한 형태로 전처리하고, 훈련과 검증에 사용할 DataLoader를 생성하는 과정을 다룬다. 각 CSV 파일에는 24개의 입력 특징(x_features)과 손 위치 정보를 나타내는 출력 목표(y_features)가 포함되어 있다. 데이터는 일정한 시간 창(TIME_STEP) 단위로 나뉘며, 이를 통해 시퀀스 기반의 시간적 관계를 학습할 수 있는 입력 데이터를 생성한다.
먼저 preprocess_file 함수는 개별 CSV 파일을 읽어 입력과 출력 데이터를 시퀀스 단위로 생성한다. 이 과정에서 create_dataset 함수는 TIME_STEP=3, STRIDE=1로 설정된 경우, 데이터의 일정 구간을 슬라이딩 윈도우 방식으로 나누어 입력 시퀀스(X)와 그에 대응하는 출력(y)을 생성한다. 예를 들어, 데이터가 5개의 시간 단위로 묶여 있다면, 첫 번째 시퀀스는 입력값으로 X=[t1, t2, t3]와 출력값으로 y=t4를, 두 번째 시퀀스는 X=[t2, t3, t4]와 y=t5를 생성한다.
그다음, load_and_process_data 함수는 여러 개의 CSV 파일 데이터를 처리하여 통합하고, 훈련 데이터와 검증 데이터로 나눈다. 이 함수는 각각의 파일을 전처리한 데이터를 하나로 합치고, 이를 다시 훈련 세트(90%)와 검증 세트(10%)로 분할한다. 이후, DataLoader를 통해 데이터를 배치 단위로 나누어 모델에 전달할 수 있도록 구성한다.
또한, 데이터셋이 매우 큰 경우를 대비해 create_final_loaders 함수는 CSV 파일들을 소규모 그룹으로 나눈 후, 각 그룹을 개별적으로 처리하여 데이터 로더를 생성한다. 이렇게 생성된 DataLoader들은 최종적으로 합쳐져 하나의 훈련용 DataLoader와 검증용 DataLoader를 구성한다. 이를 통해 대규모 데이터셋을 효율적으로 처리할 수 있다.
결과적으로 이 코드는 IMU와 VR 손 추적 데이터의 전처리부터 DataLoader 생성까지의 과정을 체계적으로 구성하며, 이를 통해 LSTM 모델이 배치 단위로 데이터를 학습하고, 검증 데이터를 통해 성능을 평가할 수 있도록 지원한다. 대규모 데이터를 효과적으로 다루기 위해 파일을 분산 처리하는 구조가 포함되어 있어 메모리와 연산 효율성을 높이는 데에도 기여한다.
4. 모델 학습
LSTM 모델의 형태는 다음과 같다.
import torch.nn as nn
class LSTMModel(nn.Module):
def __init__(self, input_size, hidden_layer_size, output_size):
super(LSTMModel, self).__init__()
self.hidden_layer_size = hidden_layer_size
self.output_size = output_size
self.lstm = nn.LSTM(input_size, hidden_layer_size,
num_layers=2, batch_first=True, bidirectional=False) # Note the batch_first=True
self.fc1 = nn.Linear(hidden_layer_size, hidden_layer_size)
self.fc2 = nn.Linear(hidden_layer_size, hidden_layer_size//2)
self.fc3 = nn.Linear(hidden_layer_size//2, output_size)
def forward(self, x):
x, _ = self.lstm(x)
x = self.fc1(x)
x= self.fc2(x)
x = self.fc3(x)
x= x[:,-1,:]
return x
위 코드에서 정의된 LSTMModel은 시계열 데이터를 처리하고 예측하는 데 사용되는 모델이다. 이 모델은 LSTM(Long Short-Term Memory) 층과 세 개의 Fully Connected 층으로 구성되어 있으며, 입력 시퀀스를 처리하여 최종 예측값을 출력한다. 모델의 주요 구성 요소는 다음과 같다.
먼저, LSTM 층은 시퀀스 데이터를 처리하며 시간적 의존성을 학습한다. 입력 데이터는 (배치 크기, 시퀀스 길이, 특성 크기)의 형식을 가지며, 이 형식이 batch_first=True 옵션에 의해 지원된다. num_layers=2로 설정하여 두 계층의 LSTM이 연속적으로 학습할 수 있도록 구성되어 있으며, 각 계층은 hidden_layer_size 차원의 은닉 상태를 유지한다. 단방향 LSTM으로 설정된 이 모델은 시간의 순서를 따라 데이터를 처리한다.
LSTM 층을 통과한 출력은 Fully Connected 층으로 전달되어 추가적인 비선형 변환을 거친다. 세 개의 FC 층은 각각 차원을 조정하며 데이터를 압축하고, 최종적으로 예측해야 할 크기인 output_size로 변환한다. 예를 들어, LSTM 층의 출력이 (배치 크기, 시퀀스 길이, hidden_layer_size) 형식이라면, Fully Connected 층을 거친 후에는 (배치 크기, 시퀀스 길이, output_size) 형식으로 변환된다. 이후 마지막 타임스텝의 출력만 선택하여 최종적으로 (배치 크기, output_size) 형식의 예측값을 생성한다. 이는 시퀀스 전체를 고려한 후 가장 최근의 정보를 기반으로 예측을 수행하기 위함이다.
이 모델은 LSTM 층의 시간적 의존성 학습 능력과 Fully Connected 층의 차원 축소 및 최적화를 조합하여 예측 정확도를 높인다. 또한, LSTM 계층 수(num_layers)나 Fully Connected 층의 구조를 조정함으로써 더 복잡한 시계열 데이터에 적응할 수 있는 유연성을 제공한다. 이러한 설계는 시계열 데이터 예측 및 분석에서 효율적인 성능을 발휘하도록 돕는다.
model = LSTMModel(input_size=INPUT_SIZE, hidden_layer_size=HIDDEN_LAYER_SIZE, output_size=OUTPUT_SIZE)
## if you have GPU
if cuda_available:
model.cuda()
epochs = EPOCHS
best_val_rmse = float('inf')
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNNG_RATE)
for epoch in range(epochs):
model.train()
train_loss= 0
for X_batch, y_batch in tqdm(trainloader, desc=f"Epoch {epoch+1}/{epochs} Training"):
## if you have GPU
if cuda_available:
X_batch, y_batch = X_batch.cuda(), y_batch.cuda()
####### LSTM ########
optimizer.zero_grad()
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
loss.backward()
optimizer.step()
train_loss += loss.item()
# run validation every 5 epochs
if epoch % 5 == 0 or epoch == epochs-1:
model.eval()
val_rmse = []
y_preds = []
y_actuals = []
with torch.no_grad():
for X_batch, y_batch in tqdm(validloader, desc=f"Epoch {epoch+1}/{epochs} Validation"):
## if you have GPU
if cuda_available:
X_batch, y_batch = X_batch.cuda(), y_batch.cuda()
# inference the model
y_pred = model(X_batch)
# calculate RMSE
rmse = torch.sqrt(criterion(y_pred, y_batch)).cpu().numpy()
val_rmse.append(rmse)
# for the first batch
if len(y_preds) == 0:
y_preds = y_pred.cpu().numpy()
y_actuals = y_batch.cpu().numpy()
# for the rest of the batches
else:
y_preds = np.vstack((y_preds, y_pred.cpu().numpy()))
y_actuals = np.vstack((y_actuals, y_batch.cpu().numpy()))
epoch_val_rmse = np.mean(val_rmse)
print(f"Epoch {epoch+1}, Validation RMSE: {epoch_val_rmse}")
if epoch_val_rmse < best_val_rmse:
best_val_rmse = epoch_val_rmse
print(f"New best model with RMSE: {best_val_rmse}, saving model...")
torch.save(model.state_dict(), OUTPUT_FILENAME)
이 코드는 LSTM 모델을 학습시키고 검증 데이터로 성능을 평가하며, 최적의 모델을 저장하는 과정을 처리한다. 전체적인 흐름은 GPU 사용 여부를 확인한 후 모델을 초기화하고, 에포크 단위로 학습 및 검증을 진행하며, 성능 향상이 있을 경우 모델을 저장하는 방식으로 구성되어 있다.
우선, 모델인 LSTMModel이 주어진 입력 크기(INPUT_SIZE), 은닉층 크기(HIDDEN_LAYER_SIZE), 출력 크기(OUTPUT_SIZE)를 기반으로 생성된다. 이후 GPU 사용이 가능한 경우 model.cuda()를 호출하여 모델을 GPU로 이동시킨다. 손실 함수는 평균 제곱 오차(MSE)를 사용하며, 옵티마이저로는 Adam이 설정된다.
학습 과정은 지정된 에포크 수(EPOCHS) 동안 반복된다. 각 에포크에서 모델은 학습 모드(model.train())로 전환되며, 학습 데이터 로더(trainloader)에서 배치 단위로 데이터를 가져와 학습한다. 배치 데이터는 GPU 사용 여부에 따라 적절한 장치로 이동되며, 모델을 통해 예측값을 생성한 후, 손실 함수(criterion)를 통해 손실을 계산한다. 이 손실은 역전파(loss.backward())를 통해 가중치에 대한 그래디언트를 계산하고, 옵티마이저(optimizer.step())가 이를 기반으로 모델의 가중치를 업데이트한다.
검증은 매 5번째 에포크마다 또는 마지막 에포크에서 수행된다. 모델은 평가 모드(model.eval())로 전환되고, 검증 데이터 로더(validloader)를 사용하여 데이터를 배치 단위로 예측한다. 이 과정에서 GPU 사용 여부에 따라 데이터를 적절히 이동하며, 각 배치에 대해 RMSE(Root Mean Squared Error)를 계산하여 저장한다. 모든 배치의 RMSE를 평균 내어 에포크의 검증 성능(epoch_val_rmse)을 평가한다.
검증 성능이 이전 에포크보다 개선되었다면(즉, epoch_val_rmse가 best_val_rmse보다 작다면), 새로운 최적 모델로 간주하여 해당 모델을 파일(OUTPUT_FILENAME)에 저장한다.
이 과정은 모델이 학습 중 과적합을 방지하고 최적 성능을 가진 모델을 유지하도록 설계되어 있으며, 검증 데이터의 성능 변화를 지속적으로 모니터링하여 학습을 조정할 수 있게 한다.
5. 평가 및 시각화
import matplotlib.pyplot as plt
model.eval()
data = pd.read_csv(r'data/free/free1/4_rear.csv') # choice was arbitrary selection from the dataset
X = data.values[:,x_features].astype(np.float32)
y = data.values[:,y_features].astype(np.float32)
y_pred = []
error_sum = 0
with torch.no_grad():
for i in tqdm(range(X.shape[0]-5)):
t = torch.Tensor(X[i:i+5])
t = t.unsqueeze(0)
## if you have GPU
if cuda_available:
t = t.cuda()
pred = model(t)
pred = pred.cpu()
pred = pred.squeeze().cpu()
y_pred.append(pred.numpy())
rmse = np.sqrt(np.sum((y[i] - pred.numpy()) ** 2) / y[i].size)
error_sum += rmse
error_sum = error_sum / (X.shape[0]-5)
print(f'RMSE avg: {error_sum}')
plt.plot(y[5:], label='actual')
y_pred_arr = np.array(y_pred)
plt.plot(y_pred_arr, label='predicted')
plt.legend(loc='best')
plt.show(block=True)
위 코드는 학습된 LSTM 모델을 사용하여 예측 결과를 검증하고, 실제 값과의 비교를 시각화하는 과정이다. 이 코드의 주요 흐름은 데이터를 불러와 모델에 입력으로 사용하고, 예측값과 실제값 간의 오차를 측정한 뒤 그래프를 통해 결과를 확인하는 것이다.
먼저, model.eval()을 호출하여 모델을 평가 모드로 전환한다. 평가 모드는 드롭아웃이나 배치 정규화와 같은 학습 전용 레이어의 동작을 비활성화한다. 그런 다음, data에 특정 CSV 파일 데이터를 불러오며, x_features와 y_features를 기반으로 입력(X)과 출력(y) 데이터를 추출한다. 이 데이터는 학습에 사용된 것과 동일한 형식으로 정규화되어야 한다.
예측 과정에서는 데이터의 시퀀스를 슬라이딩 윈도우 방식으로 생성한다. for 루프는 데이터의 전체 길이에서 5 타임스텝 단위로 입력 시퀀스를 추출하며 진행된다. 각 시퀀스는 (1, 5, feature_size)의 3차원 텐서로 변환되고, GPU 사용 여부를 확인하여 CUDA 텐서로 변환된다. 이후, 학습된 LSTM 모델에 입력되어 예측값을 생성한다. 예측값은 모델의 마지막 Fully Connected 층에서 출력되며, GPU 상에서 실행되었더라도 CPU로 다시 이동한 뒤 numpy 형식으로 변환된다.
루프를 통해 생성된 각 예측값은 리스트에 추가되며, 동시에 실제값과의 Root Mean Square Error(RMSE)를 계산한다. RMSE는 예측 정확도를 나타내는 지표로, 작은 값일수록 모델의 성능이 우수함을 의미한다. 모든 데이터에 대해 RMSE를 누적하여 평균값(error_sum)을 출력함으로써 모델의 전반적인 성능을 평가한다.
마지막으로, 예측값(y_pred)과 실제값(y)을 비교하는 시각화를 수행한다. 그래프에서는 파란 선으로 실제값을, 주황 선으로 예측값을 표시한다. 두 곡선의 유사도가 높을수록 모델의 예측 정확도가 높음을 의미한다. 이 시각화는 모델의 성능을 직관적으로 이해하고, 개선 방향을 탐색하는 데 유용하다.
요약 : 이 코드는 IMU 센서와 VR 손 추적 데이터를 이용해 LSTM 기반의 예측 모델을 학습시키는 과정을 다룬다. 전체 프로세스는 데이터 전처리, 학습 데이터 생성, 모델 학습 및 검증, 그리고 최적 모델 저장이라는 주요 단계를 포함한다.
먼저 데이터는 여러 개의 CSV 파일로 제공되며, 각 파일은 시간에 따른 센서 데이터와 그에 대응하는 손 위치 정보를 포함한다. 예를 들어, 하나의 데이터 파일에서 IMU 센서의 24개 특징 값이 기록되고, 목표 출력은 손의 위치로 나타난다. 이 데이터를 학습 가능한 형태로 변환하기 위해 일정한 시간 구간(TIME_STEP=5) 단위로 슬라이딩 윈도우 방식을 사용한다. 즉, 첫 5개의 시간 데이터를 입력(X)으로 사용하고, 그 다음 시간 데이터를 출력(y)으로 설정한다. 이 과정을 모든 데이터에 적용해 시퀀스 기반의 학습 데이터를 생성한다.
이후 생성된 데이터를 통합하고, 이를 훈련 세트(90%)와 검증 세트(10%)로 분리한다. 데이터를 모델에 효율적으로 전달하기 위해 PyTorch의 DataLoader를 활용해 데이터를 배치(batch) 단위로 나누고, 모델 학습 중 무작위로 순서를 섞어 과적합을 방지한다. 데이터가 매우 클 경우, CSV 파일을 작은 그룹으로 나누어 처리한 뒤, 모든 그룹을 합쳐 최종 DataLoader를 생성해 대규모 데이터를 효과적으로 다룬다.
LSTM 모델은 입력 크기(센서 특징의 개수), 은닉층 크기, 출력 크기를 설정해 정의되며, GPU 사용이 가능할 경우 GPU로 이동해 연산을 가속화한다. 모델 학습은 지정된 에포크 동안 수행되며, 각 배치 데이터를 모델에 입력해 손실(MSELoss)을 계산하고, 이를 기반으로 모델의 가중치를 업데이트한다. 매 5번째 에포크마다 검증 데이터를 사용해 모델 성능을 평가하고, RMSE(Root Mean Squared Error)를 기준으로 최적 모델을 저장한다. 이렇게 하면 과적합을 방지하고, 검증 데이터에서 가장 높은 성능을 보이는 모델을 선택할 수 있다.
학습이 완료된 후, 모델을 사용해 임의의 테스트 데이터 파일을 예측해 실제 값과 비교한다. 예를 들어, 특정 CSV 파일의 데이터를 기반으로 손 위치를 예측하고 이를 시각화하여 모델의 성능을 확인할 수 있다. 이 과정에서 평균 RMSE를 계산하여 모델의 전체적인 정확도를 평가한다.
이 코드의 모든 과정은 IMU 센서와 VR 데이터를 결합하여 신뢰할 수 있는 손 추적 예측 모델을 개발하기 위한 것으로, 데이터 전처리부터 모델 학습과 검증, 그리고 테스트까지 체계적으로 구성되어 있다.
개선