지난 포스트에서 수집한 데이터셋을 가지고 LSTM으로 학습을 진행하고 결과까지 확인해보려고 한다.
actions = [
'a',
'b',
'c'
]
data = np.concatenate([
np.load('dataset/seq_a_1650133564.npy'),
np.load('dataset/seq_b_1650133564.npy'),
np.load('dataset/seq_c_1650133564.npy')
], axis=0)
data.shape
-> 만들었던 데이터 셋을 로드하고 하나로 합쳐준다.
x_data = data[:, :, :-1]
labels = data[:, 0, -1]
print(x_data.shape)
print(labels.shape)
-> 데이터셋에는 label값이 포함되어있다. 마지막값이 label값이기 때문에 빼주고, 마지막 값만 label로 만들어 분리한다.
from tensorflow.keras.utils import to_categorical
y_data = to_categorical(labels, num_classes=len(actions))
y_data.shape
-> One-hot encoding을 한다. keras에 to_categorical을 사용
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)
-> training set과 test set을 나눠준다. sklearn의 train_test_split 사용
training set은 90%, test set은 10%로 설정했다.
pip install scikit-lean
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]), # node 개수 64개
Dense(32, activation='relu'), # node 개수 32개
Dense(len(actions), activation='softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc']) # loss='categorical_crossentropy -> 3개의 action 중 어떤 건지 모델에게 추론하게 함
model.summary()
-> model 정의, Sequential API를 사용했고 LSTM과 DENSE를 연결한다.
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')
]
)
-> 학습 과정, 200번 epoch을 돈다. 학습이 완료되면 model을 저장한다.
One Epoch is when an ENTIRE dataset is passed forward and backward through the neural network only ONCE
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()
-> 학습이 완료되면 그래프를 그린다.
import cv2
import mediapipe as mp
import numpy as np
from tensorflow.keras.models import load_model
actions = ['a', 'b', 'c']
seq_length = 30
model = load_model('models/model.h5')
# MediaPipe hands model
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
cap = cv2.VideoCapture(0)
# w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
# out = cv2.VideoWriter('input.mp4', fourcc, cap.get(cv2.CAP_PROP_FPS), (w, h))
# out2 = cv2.VideoWriter('output.mp4', fourcc, cap.get(cv2.CAP_PROP_FPS), (w, h))
seq = []
action_seq = []
while cap.isOpened():
ret, img = cap.read()
img0 = img.copy()
img = cv2.flip(img, 1)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
result = hands.process(img)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
if result.multi_hand_landmarks is not None:
for res in result.multi_hand_landmarks:
joint = np.zeros((21, 4))
for j, lm in enumerate(res.landmark):
joint[j] = [lm.x, lm.y, lm.z, lm.visibility]
# Compute angles between joints
v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19], :3] # Parent joint
v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20], :3] # Child joint
v = v2 - v1 # [20, 3]
# Normalize v
v = v / np.linalg.norm(v, axis=1)[:, np.newaxis]
# Get angle using arcos of dot product
angle = np.arccos(np.einsum('nt,nt->n',
v[[0,1,2,4,5,6,8,9,10,12,13,14,16,17,18],:],
v[[1,2,3,5,6,7,9,10,11,13,14,15,17,18,19],:])) # [15,]
angle = np.degrees(angle) # Convert radian to degree
d = np.concatenate([joint.flatten(), angle])
seq.append(d)
mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS)
if len(seq) < seq_length:
continue
input_data = np.expand_dims(np.array(seq[-seq_length:], dtype=np.float32), axis=0)
y_pred = model.predict(input_data).squeeze()
i_pred = int(np.argmax(y_pred))
conf = y_pred[i_pred]
if conf < 0.9:
continue
action = actions[i_pred]
action_seq.append(action)
if len(action_seq) < 3:
continue
this_action = '?'
if action_seq[-1] == action_seq[-2] == action_seq[-3]:
this_action = action
cv2.putText(img, f'{this_action.upper()}', org=(int(res.landmark[0].x * img.shape[1]), int(res.landmark[0].y * img.shape[0] + 20)), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2)
# out.write(img0)
# out2.write(img)
cv2.imshow('img', img)
if cv2.waitKey(1) == ord('q'):
break
Traceback (most recent call last):
File "c:/Users/JH/Documents/VSCodeSonsuProjects/GESTURE_RECOGNITION/test.py", line 9, in <module>
model = load_model('model.h5')
File "C:\Users\JH\AppData\Local\Programs\Python\Python38\lib\site-packages\tensorflow\python\keras\saving\save.py", line 211, in load_model
loader_impl.parse_saved_model(filepath)
File "C:\Users\JH\AppData\Local\Programs\Python\Python38\lib\site-packages\tensorflow\python\saved_model\loader_impl.py", line 111, in parse_saved_model
raise IOError("SavedModel file does not exist at: %s/{%s|%s}" %
OSError: SavedModel file does not exist at: model.h5/{saved_model.pbtxt|saved_model.pb}
OSError: SavedModel file does not exist at: model.h5/{saved_model.pbtxt|saved_model.pb}
해결책 1. pip install h5py
-> 이미 설치되어있다.
https://stackoverflow.com/questions/61699140/oserror-savedmodel-file-does-not-exist-at-dnn-mpg-model-h5-saved-model-pbt/65014543#65014543
https://arca.live/b/programmers/44902070?p=1
해결책 2. NodeJS에서 실행하는 PythonShell에서는 H5모델이 아닌 Savedmodel만 읽을 수 있다.
https://blog.naver.com/PostView.naver?blogId=heyji1230&logNo=222146936643&redirect=Dlog&widgetTypeCall=true&directAccess=false
https://www.tensorflow.org/guide/keras/save_and_serialize?hl=ko
따라서 위의 train에서는 모델을 h5로 저장하고 있기 때문에 savedmodel로 변환해주는 코드를 train.ipynb에 추가했다.
import tensorflow as tf
model = tf.keras.models.load_model('model.h5')
tf.saved_model.save('model')
-> 이 코드를 실행하면 model이라는 폴더가 생성되고 내부에 assets폴더와 variables폴더, saved_model.pb 파일이 생성된다.
하지만 여전히 같은 오류가 발생했다. 구글링을 하다보니 savedModel을 load하는 함수와 h5를 load하는 함수가 다르다는 생각이 들었고 해당 내용을 찾아봤다.
https://www.tensorflow.org/api_docs/python/tf/saved_model/load : Loading Keras models을 참고했다.
test.py에서 model을 load 하는 코드를 다음 코드로 수정하였다.
model = tf.keras.Model(...)
=> 오류 해결!
NotImplementedError: When subclassing the `Model` class, you should implement a `call` method.
def call(self, inputs, *args, **kwargs):
return self.model(inputs)
-> 출처
def call(self, inputs):
self.total.assign_add(tf.reduce_sum(inputs, axis=0))
return self.total
-> 출처
import tensorflow as tf
class MyModel(tf.keras.Model):
def __init__(self):
super().__init__()
self.dense1 = tf.keras.layers.Dense(4, activation=tf.nn.relu)
self.dense2 = tf.keras.layers.Dense(5, activation=tf.nn.softmax)
def call(self, inputs):
x = self.dense1(inputs)
return self.dense2(x)
model = MyModel()
-> 출처
오류해결 진행중
참고
[기초통계] 수치형 자료(numerical data)와 범주형 자료(categorical data)
머신 러닝 - epoch, batch size, iteration의 의미