Gesture recognition using muscle activity¶
Credit: AITS Cainvas Community
Electromyography is a technique for recording and evaluating electrical acitvity produced in the skeletal muscles.
The readings from the muscle activity sensor is then fed to a model that can be trained on gestures, separately for each user (customization for a user) or to obtain a pre trained model ready for direct implementation in various applications like prosthetic arms, game control etc.
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.model_selection import train_test_split
from tensorflow.keras import layers, models, losses, callbacks, optimizers
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import random
The datatset¶
Four different motion gestures were recorded using the MYO armband.
Each reading has 8 consecutive readings of all 8 sensors resulting in 64 columns of EMG data. The last column is the gesture corresponding to the readings.
!wget -N "https://cainvas-static.s3.amazonaws.com/media/user_data/cainvas-admin/muscle_gesture.zip"
!unzip -o "muscle_gesture.zip"
!rm "muscle_gesture.zip"
df0 = pd.read_csv('muscle_gesture/0.csv', header = None)
df1 = pd.read_csv('muscle_gesture/1.csv', header = None)
df2 = pd.read_csv('muscle_gesture/2.csv', header = None)
df3 = pd.read_csv('muscle_gesture/3.csv', header = None)
df = pd.concat([df0, df1, df2, df3])
df
df[64].value_counts()
Its a fairly balanced dataset.
df.describe()
The column values are have different range (min, max) and varying standard deviations.
These can be standardized to have mean=0 and sd=1 using the StandardScaler later in the notebook.
Defining the input and output¶
X = df[df.columns.tolist()[:-1]]
X
y = pd.get_dummies(df[64])
y
# Splitting into train, val and test set -- 80-10-10 split
# First, an 80-20 split
Xtrain, Xvaltest, ytrain, yvaltest = train_test_split(X, y, test_size = 0.2)
# Then split the 20% into half
Xval, Xtest, yval, ytest = train_test_split(Xvaltest, yvaltest, test_size = 0.5)
print("Number of samples in...")
print("Training set: ", Xtrain.shape, ytrain.shape)
print("Validation set: ", Xval.shape, yval.shape)
print("Testing set: ", Xtest.shape, ytest.shape)
Normalization¶
To obtain mean = 0 and standard deviation = 1
ss_scaler = StandardScaler()
# Fit on training set alone
Xtrain = ss_scaler.fit_transform(Xtrain)
# Use it to transform val and test input
Xval = ss_scaler.transform(Xval)
Xtest = ss_scaler.transform(Xtest)
The model¶
model = models.Sequential([
layers.Dense(256, activation = 'relu', input_shape = Xtrain[0].shape),
layers.Dense(64, activation = 'relu'),
layers.Dense(16, activation = 'relu'),
layers.Dense(4, activation = 'softmax')
])
cb = [callbacks.EarlyStopping(patience = 5, restore_best_weights = True)]
model.summary()
model.compile(optimizer=optimizers.Adam(0.0001), loss=losses.CategoricalCrossentropy(), metrics=['accuracy'])
history = model.fit(Xtrain, ytrain, epochs = 256, validation_data = (Xval, yval), callbacks = cb)
model.evaluate(Xtest, ytest)
cm = confusion_matrix(np.argmax(np.array(ytest), axis = 1), np.argmax(model.predict(Xtest), axis = 1))
cm = cm.astype('int') / cm.sum(axis=1)[:, np.newaxis]
fig = plt.figure(figsize = (5, 5))
ax = fig.add_subplot(111)
for i in range(cm.shape[1]):
for j in range(cm.shape[0]):
if cm[i,j] > 0.8:
clr = "white"
else:
clr = "black"
ax.text(j, i, format(cm[i, j], '.2f'), horizontalalignment="center", color=clr)
_ = ax.imshow(cm, cmap=plt.cm.Blues)
ax.set_xticks(range(4))
ax.set_yticks(range(4))
ax.set_xticklabels(range(4), rotation = 90)
ax.set_yticklabels(range(4))
plt.xlabel('Predicted')
plt.ylabel('True')
plt.show()
Plotting the metrics¶
def plot(history, variable, variable2):
plt.plot(range(len(history[variable])), history[variable])
plt.plot(range(len(history[variable2])), history[variable2])
plt.legend([variable, variable2])
plt.title(variable)
plot(history.history, "accuracy", 'val_accuracy')
plot(history.history, "loss", "val_loss")
Predictions¶
# pick random test data sample from one batch
x = random.randint(0, len(Xtest) - 1)
output = model.predict(Xtest[x].reshape(1, -1))[0]
pred = np.argmax(output)
print("Predicted: ", pred, "(", output[pred], ")")
print("True: ", np.argmax(np.array(ytest)[x]))
deepC¶
model.save('muscle_gesture.h5')
!deepCC muscle_gesture.h5
x = random.randint(0, len(Xtest) - 1)
np.savetxt('sample.data', Xtest[x]) # xth sample into text file
# run exe with input
!muscle_gesture_deepC/muscle_gesture.exe sample.data
# show predicted output
nn_out = np.loadtxt('deepSea_result_1.out')
pred = np.argmax(nn_out)
print("Predicted: ", pred, "(", nn_out[pred], ")")
print("True: ", np.argmax(np.array(ytest)[x]))