본문 바로가기

인공지능/인공지능

AI 프로젝트

깃허브에서 가져온 기존 모델을 활용하여 최적화하는 과정이다

이 코드 전에 수집 데이터는 내가 원하는 제스처 데이터가 없어서 수작업으로 제스처 데이터를 모았다.

 

기존코드

import numpy as np
import os

os.environ['CUDA_VISIBLE_DEVICES'] = '1'
os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true'


actions = [
    'Lv1', 'Lv2', 'Lv3'
]

data = np.concatenate([
    np.load('dataset/seq_Lv1_1742691808.npy'),
    np.load('dataset/seq_Lv2_1742691808.npy'),
    np.load('dataset/seq_Lv3_1742691808.npy')
], axis=0)

data.shape



x_data = data[:, :, :-1]
labels = data[:, 0, -1]

print(x_data.shape)
print(labels.shape)


from tensorflow.keras.utils import to_categorical

y_data = to_categorical(labels, num_classes=len(actions))
y_data.shape



from sklearn.model_selection import train_test_split

x_data = x_data.astype(np.float32)
y_data = y_data.astype(np.float32)

x_train, x_val, y_train, y_val = train_test_split(x_data, y_data, test_size=0.1, random_state=2021)

print(x_train.shape, y_train.shape)
print(x_val.shape, y_val.shape)


from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense

model = Sequential([
    LSTM(64, activation='relu', input_shape=x_train.shape[1:3]),
    Dense(32, activation='relu'),
    Dense(len(actions), activation='softmax')
])

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
model.summary()


from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau

history = model.fit(
    x_train,
    y_train,
    validation_data=(x_val, y_val),
    epochs=200,
    callbacks=[
        ModelCheckpoint('models/model.h5', monitor='val_acc', verbose=1, save_best_only=True, mode='auto'),
        ReduceLROnPlateau(monitor='val_acc', factor=0.5, patience=50, verbose=1, mode='auto')
    ]
)


import matplotlib.pyplot as plt

fig, loss_ax = plt.subplots(figsize=(16, 10))
acc_ax = loss_ax.twinx()

loss_ax.plot(history.history['loss'], 'y', label='train loss')
loss_ax.plot(history.history['val_loss'], 'r', label='val loss')
loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
loss_ax.legend(loc='upper left')

acc_ax.plot(history.history['acc'], 'b', label='train acc')
acc_ax.plot(history.history['val_acc'], 'g', label='val acc')
acc_ax.set_ylabel('accuracy')
acc_ax.legend(loc='upper left')

plt.show()


from sklearn.metrics import multilabel_confusion_matrix
from tensorflow.keras.models import load_model

model = load_model('models/model.h5')

y_pred = model.predict(x_val)

multilabel_confusion_matrix(np.argmax(y_val, axis=1), np.argmax(y_pred, axis=1))

코드 변경

import numpy as np
import os
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping
from sklearn.metrics import multilabel_confusion_matrix
import matplotlib.pyplot as plt
from tensorflow.keras.models import load_model

os.environ['CUDA_VISIBLE_DEVICES'] = '1'
os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true'

actions = ['Lv1', 'Lv2', 'Lv3']

data = np.concatenate([
    np.load('dataset/seq_Lv1_1742691808.npy'),
    np.load('dataset/seq_Lv2_1742691808.npy'),
    np.load('dataset/seq_Lv3_1742691808.npy')
], axis=0)

print(f"Data shape: {data.shape}")

x_data = data[:, :, :-1]
labels = data[:, 0, -1]

y_data = to_categorical(labels, num_classes=len(actions))

x_data = x_data.astype(np.float32)
y_data = y_data.astype(np.float32)
x_train, x_val, y_train, y_val = train_test_split(x_data, y_data, test_size=0.1, random_state=2021)

#모델 구조 정의
model = Sequential([
    LSTM(128, activation='tanh', input_shape=x_train.shape[1:3], return_sequences=True),  
    Dropout(0.2),  #Dropout 추가/ 과적합 방지
    LSTM(64, activation='tanh'),  #LSTM 층 2개 추가
    Dropout(0.2),  #Dropout 추가
    Dense(32, activation='relu'),
    Dense(len(actions), activation='softmax')
])

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])

model.summary()

callbacks = [
    ModelCheckpoint('models/model.h5', monitor='val_acc', verbose=1, save_best_only=True, mode='auto'),
    ReduceLROnPlateau(monitor='val_acc', factor=0.5, patience=50, verbose=1, mode='auto'),
    EarlyStopping(monitor='val_loss', patience=30, verbose=1, mode='auto')  # EarlyStopping 추가
]

#모델 학습
history = model.fit(
    x_train, 
    y_train, 
    validation_data=(x_val, y_val), 
    epochs=200, 
    batch_size=64,  #배치 사이즈 추가
    callbacks=callbacks
)

        #훈련 과정 시각화
fig, loss_ax = plt.subplots(figsize=(16, 10))
acc_ax = loss_ax.twinx()

loss_ax.plot(history.history['loss'], 'y', label='train loss')
loss_ax.plot(history.history['val_loss'], 'r', label='val loss')
loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
loss_ax.legend(loc='upper left')

acc_ax.plot(history.history['acc'], 'b', label='train acc')
acc_ax.plot(history.history['val_acc'], 'g', label='val acc')
acc_ax.set_ylabel('accuracy')
acc_ax.legend(loc='upper left')

plt.show()

model = load_model('models/model.h5')

y_pred = model.predict(x_val)

mcm = multilabel_confusion_matrix(np.argmax(y_val, axis=1), np.argmax(y_pred, axis=1))
print(mcm)

개선 방향

1. LSTM 층 활성화 함수 변경 (tanh vs relu)

LSTM 네트워크에서 relu 활성화 함수는 보통 잘 동작하지 않는다.

순환 신경망(RNN)에서는 장기적인 의존성을 처리하는 데 어려움이 있을 수 있다

기존 activation='relu'
변경 activation='tanh'

 

2. LSTM 층 추가 및 Dropout 사용

원래의 모델은 LSTM 층을 하나만 사용하고 있었기 때문에 복잡한 시계열 데이터를 처리하는 데 한계가 있었을 수 있고 과적합이 발생할 가능성이 있다. 과적합은 모델이 훈련 데이터에 너무 맞춰져서 조금만 값이 바뀌어도 맞추지 못하는 등 실제 데이터에서 성능이 떨어지는 문제를 일으킨다

LSTM(128, activation='tanh', input_shape=x_train.shape[1:3], return_sequences=True),
Dropout(0.2),  #Dropout을 추가해서 과적합 방지

Dropout : 임의로 일부 뉴런을 비활성화하여 네트워크가 특정 입력에 과도하게 의존하지 않도록 도움(과적합 방지)

 

3. 조기 종료(EarlyStopping) 추가

모델이 더 이상 성능이 향상되지 않거나 검증에서 오차가 증가하기 시작할때 불필요하게 학습을 더 진행할 수 있는데 이럴 때는 학습을 멈추는 것이 효율적이다

EarlyStopping콜백을 추가하여 val_loss이 일정 epoch 동안 개선되지 않으면 학습을 중단하도록 했다. 이렇게 하면 학습 시간도 단축되고, 과적합도 방지할 수 있다

    EarlyStopping(monitor='val_loss', patience=30, verbose=1, mode='auto')  
    #EarlyStopping 추가

 

4. 모델 체크포인트 및 학습률 조정

훈련 중에 성능이 좋아지지 않거나 학습률이 너무 커서 최적의 값을 찾기 어려운 경우가 발생할 수 있는데 이럴 때는 모델을 최적화하기 위해 학습률을 자동으로 조정하거나 최상의 상태에서 모델을 저장하는 것이 필요하다

val_acc가 개선될 때마다 가장 좋은 모델을 저장하게 하면 학습이 끝나고 난 후에 가장 좋은 상태의 모델을 쉽게 불러올 수있다.

    ModelCheckpoint('models/model.h5', monitor='val_acc', verbose=1, 
    save_best_only=True, mode='auto'),

 

5. 배치 크기(batch_size) 조정

배치 크기가 너무 작거나 크면, 훈련 속도나 성능에 문제가 생길 수 있어서 batch_size를 64로 설정하여 적당한 크기로 훈련을 진행했다. 대체로 모델 성능과 훈련 속도 간의 균형을 잘 맞출 수 있는 값이다.

 

#모델 학습 코드
history = model.fit(
    x_train, 
    y_train, 
    validation_data=(x_val, y_val), 
    epochs=200, 
    batch_size=64,  #배치 사이즈 64로 추가
    callbacks=callbacks
)

기존의 코드와 변경 후 코드의 훈련과정을 비교

개선 전

 

개선 후