2025 november
This project aims to build a model that predicts the human activities such as Walking, Walking Upstairs, Walking Downstairs, Sitting, Standing and Laying from the Sensor data of smart phones.
The repository has 3 ipython notebook
1 HAR_EDA.ipynb : Data pre-processing and Exploratory Data Analysis
2 HAR_PREDICTION_MODELS.ipynb : Machine Learning models with featured data
3 HAR_LSTM.ipynb : LSTM model on raw timeseries data
All the code is written in python 3
DEPENDENCIES
- tensorflow
- keras
- numpy
- pandas
- matplotlib
- seaborn
- sklearn
- itertools
- datetime
Every modern Smart Phone has a number of sensors. we are interested in two of the sensors Accelerometer and Gyroscope.
The data is recorded with the help of sensors
This is a 6 class classification problem as we have 6 activities to detect.
This project has two parts, the first part trains, tunes and compares Logistic Regression, Linear support vector classifier, RBF(Radial Basis Function) SVM classifier, Decision Tree, Random Forest, Gradient Boosted Decision Trees model and uses the data featured by domain expert.
The second part uses the raw time series windowed data to train (Long Short term Memory)LSTM models. The LSTM models are semi tuned manually to fast forward the tuning task.
The dataset can be downloaded from
https://archive.ics.uci.edu/ml/datasets/human+activity+recognition+using+smartphones#
dataset is also included in the Repository with in the folder UCI_HAR_Dataset
Human Activity Recognition database is built from the recordings of 30 persons performing activities of daily living (ADL) while carrying a waist-mounted smartphone with embedded inertial sensors(accelerometer and Gyroscope).
Activities
- Walking
- Walking Upstairs
- Walking Downstairs
- Sitting
- Standing
- Laying
Accelerometers detect magnitude and direction of the proper acceleration, as a vector quantity, and can be used to sense orientation (because direction of weight changes)
GyroScope maintains orientation along a axis so that the orientation is unaffected by tilting or rotation of the mounting, according to the conservation of angular momentum.
Accelerometer measures the directional movement of a device but will not be able to resolve its lateral orientation or tilt during that movement accurately unless a gyro is there to fill in that info.
With an accelerometer you can either get a really "noisy" info output that is responsive, or you can get a "clean" output that's sluggish. But when you combine the 3-axis accelerometer with a 3-axis gyro, you get an output that is both clean and responsive in the same time.
- Both sensors generate data in 3 Dimensional space over time. Hence the data captured are '3-axial linear acceleration'(tAcc-XYZ) from accelerometer and '3-axial angular velocity' (tGyro-XYZ) from Gyroscope with several variations.
- prefix 't' in those metrics denotes time.
- suffix 'XYZ' represents 3-axial signals in X , Y, and Z directions.
- The available data is pre-processed by applying noise filters and then sampled in fixed-width windows(sliding windows) of 2.56 seconds each with 50% overlap. ie., each window has 128 readings.
For each window a feature vector was obtained by calculating variables from the time and frequency domain. each datapoint represents a window with different readings.
Readings are divided into a window of 2.56 seconds with 50% overlapping.
-
Accelerometer readings are divided into gravity acceleration and body acceleration readings, which has x,y and z components each.
-
Gyroscope readings are the measure of angular velocities which has x,y and z components.
-
Jerk signals are calculated for BodyAcceleration readings.
-
Fourier Transforms are made on the above time readings to obtain frequency readings.
-
Now, on all the base signal readings., mean, max, mad, sma, arcoefficient, engerybands,entropy etc., are calculated for each window.
-
We get a feature vector of 561 features and these features are given in the dataset.
-
Each window of readings is a datapoint of 561 features,and we have 10299 readings.
-
These are the signals that we got so far.(prefix t means time domain data, prefix f means frequency domain data)
- The readings from 70% of the volunteers(21 people) were taken as trianing data and remaining 30% volunteers recordings(9 people) were taken for test data
- All the data is present in 'UCI_HAR_dataset/' folder in present working directory.
- Feature names are present in 'UCI_HAR_dataset/features.txt'
- Train Data (7352 readings)
- 'UCI_HAR_dataset/train/X_train.txt'
- 'UCI_HAR_dataset/train/subject_train.txt'
- 'UCI_HAR_dataset/train/y_train.txt'
- Test Data (2947 readinds)
- 'UCI_HAR_dataset/test/X_test.txt'
- 'UCI_HAR_dataset/test/subject_test.txt'
- 'UCI_HAR_dataset/test/y_test.txt'
For detailed code of this section you can always check the HAR_EDA Notebook
if some class have too little or too large numbers of values compared to rest of the classes than the dataset is imbalanced.
Plot-1
In this plot on the X-axis we have subjects(volunteers) 1 to 30. Each color represents an activity
On the y-axis we have amount of data for each activity by provided by each subject.
Plot-2
From plot1 and plot2 it is clear that dataset is almost balanced.
Plot-3
sns.set_palette("Set1", desat=0.80)
facetgrid = sns.FacetGrid(train, hue='ActivityName', size=6,aspect=2)
facetgrid.map(sns.distplot,'tBodyAccMagmean', hist=False)\
.add_legend()
plt.annotate("Stationary Activities", xy=(-0.956,17), xytext=(-0.9, 23), size=20,\
va='center', ha='left',\
arrowprops=dict(arrowstyle="simple",connectionstyle="arc3,rad=0.1"))
plt.annotate("Moving Activities", xy=(0,3), xytext=(0.2, 9), size=20,\
va='center', ha='left',\
arrowprops=dict(arrowstyle="simple",connectionstyle="arc3,rad=0.1"))
plt.show()The above plot is of tBodyAccMagmean which is mean values of magnitude of acceleration in time space.
Plot-4
Box plot, mean of magnitude of an acceleration
plt.figure(figsize=(7,7))
sns.boxplot(x='ActivityName', y='tBodyAccMagmean',data=train, showfliers=False, saturation=1)
plt.ylabel('Acceleration Magnitude mean')
plt.axhline(y=-0.7, xmin=0.1, xmax=0.9,dashes=(5,5), c='g')
plt.axhline(y=-0.05, xmin=0.4, dashes=(5,5), c='m')
plt.xticks(rotation=90)
plt.show()From plot-3 and plot-4 we can see that stationary activities can be linearly separated from activities with motion.
Plot-5
Dimensionality reduction using T-distributed Stochastic Neighbor Embedding (t-SNE) to visualize 561 dimension dataset.
Sitting and standing are overlapped while other 4 classes can be separated well.
scikit-learn is used for all the 6 alogorithms listed below.
Hyperparameters of all models are tuned by grid search CV
Models fitted:
- Logistic Regression
- Linear Support Vector Classifier(SVC)
- Radial Basis Function (RBF) kernel SVM classifier
- Decision Tree
- Random Forest
- Gradient Boosted DT
| model | Accuracy | Error |
|---|---|---|
| Logistic Regression | 96.27% | 3.733% |
| Linear SVC | 96.61% | 3.393% |
| rbf SVM classifier | 96.27% | 3.733% |
| Decision Tree | 86.43% | 13.57% |
| Random Forest | 91.31% | 8.687% |
| Gradient Boosted DT | 91.31% | 8.687% |
Observing the Top 2 Models
Logistic Regression
Plot-6
Normalized confusion matrix for Linear Regression Model
Diagonal Value of 1 means 100% accuracy for that class, and 0 means 0% accuracy.
considering the diagonal elements we have value 1 for rows corresponding to 'Laying' and 'Walking'.
while 'sitting' has value of only 0.87. In the row 2nd row and 3rd column we have value 0.12 which basically means about 12% readings of the class sitting is misclassified as standing.
Linear SVC
Plot-7
Normalized confusion matrix for Linear SVC Model
In this model also the diagonal elements, we have value 1 for rows corresponding to 'Laying' and 'Walking'.
Again row corresponding to 'sitting' has value of only 0.87. In the row 2nd row and 3rd column we have value 0.12 which basically means about 12% readings of the class sitting is misclassified as standing.
It is not a surprise as in the t-sne plot (plot-5) we saw that 'sitting' and 'Standing' class readings are overlapping.
For detailed code of all the ML models check the HAR_PREDICTION_MODELS Notebook
For detailed code of this section you can always check the HAR_LSTM Notebook
keras with tensorflow backend is used.
LSTM models need large amount of data to train properly, we also need to be cautious not to overfit.
The raw series data is used to train the LSTM models, and not the heavily featured data. We don't want to reduce the data available to train the model hence the test dataset is used as validation data.
dropout Layers used to keep overfitting in check.
Initialization of some of the parameters
timesteps = len(X_train[0])
input_dim = len(X_train[0][0])
n_classes = _count_classes(Y_train)
print(timesteps)
print(input_dim)
print(len(X_train))128
9
7352
LSTM model 1
This is a single LSTM(128) model
# Initiliazing the sequential model
model = Sequential()
# Configuring the parameters
model.add(LSTM(n_hidden, input_shape=(timesteps, input_dim)))
# Adding Batchnormalization
model.add(BatchNormalization())
# Adding a dropout layer
model.add(Dropout(pv))
# Adding a dense output layer with sigmoid activation
model.add(Dense(n_classes, activation='sigmoid'))
model.summary()_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_30 (LSTM) (None, 128) 70656
_________________________________________________________________
batch_normalization_10 (Batc (None, 128) 512
_________________________________________________________________
dropout_29 (Dropout) (None, 128) 0
_________________________________________________________________
dense_25 (Dense) (None, 6) 774
=================================================================
Total params: 71,942
Trainable params: 71,686
Non-trainable params: 256
_________________________________________________________________LSTM models require large amount of compute power. The following parameters are selected after some experimental runs to get a good accuracy.
epochs = 30
batch_size = 32
n_hidden = 128
pv = 0.25 # keep probability of dropout layerWith this simple LSTM(128) architecture we got 93.75% accuracy and a loss of 0.22
Confusion Matrix
| Pred /True | LAYING | SITTING | STANDING | WALKING | WALKING_DOWNSTAIRS | WALKING_UPSTAIRS |
|---|---|---|---|---|---|---|
| LAYING | 537 | 0 | 0 | 0 | 0 | 0 |
| SITTING | 5 | 390 | 93 | 0 | 0 | 3 |
| STANDING | 0 | 96 | 436 | 0 | 0 | 0 |
| WALKING | 0 | 1 | 0 | 473 | 10 | 12 |
| WALKING_DOWNSTAIRS | 0 | 0 | 0 | 0 | 420 | 0 |
| WALKING_UPSTAIRS | 0 | 0 | 0 | 0 | 1 | 470 |
LSTM model 2
This model has 2 LSTM layers LSTM(128) and LSTM(64) stacked.
# Initiliazing the sequential model
model1 = Sequential()
# Configuring the parameters
model1.add(LSTM(n_hidden1, return_sequences=True, input_shape=(timesteps, input_dim)))
# Adding a dropout layer
model1.add(Dropout(pv1))
model1.add(LSTM(n_hidden2))
# Adding a dropout layer
model1.add(Dropout(pv2))
# Adding a dense output layer with sigmoid activation
model1.add(Dense(n_classes, activation='sigmoid'))
model1.summary()_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_28 (LSTM) (None, 128, 128) 70656
_________________________________________________________________
dropout_27 (Dropout) (None, 128, 128) 0
_________________________________________________________________
lstm_29 (LSTM) (None, 64) 49408
_________________________________________________________________
dropout_28 (Dropout) (None, 64) 0
_________________________________________________________________
dense_24 (Dense) (None, 6) 390
=================================================================
Total params: 120,454
Trainable params: 120,454
Non-trainable params: 0
_________________________________________________________________The following parameters are selected after some experimental runs to get a good accuracy.
epochs1 = 30
batch_size1= 32
n_hidden1 = 128
n_hidden2 =64
pv1 = 0.2
pv2 = 0.5With this simple LSTM architecture we got 93.17% accuracy and a loss of 0.28
Confusion Matrix
| Pred /True | LAYING | SITTING | STANDING | WALKING | WALKING_DOWNSTAIRS | WALKING_UPSTAIRS |
|---|---|---|---|---|---|---|
| LAYING | 510 | 0 | 1 | 0 | 0 | 26 |
| SITTING | 0 | 402 | 86 | 1 | 0 | 2 |
| STANDING | 0 | 76 | 454 | 1 | 0 | 1 |
| WALKING | 0 | 0 | 0 | 468 | 25 | 3 |
| WALKING_DOWNSTAIRS | 0 | 0 | 0 | 1 | 418 | 1 |
| WALKING_UPSTAIRS | 0 | 2 | 0 | 15 | 33 | 421 |
https://en.wikipedia.org/wiki/Gyroscope
https://scikit-learn.org/stable/supervised_learning.html#supervised-learning
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
https://keras.io/getting-started/sequential-model-guide/
https://machinelearningmastery.com/dropout-regularization-deep-learning-models-keras/
https://appliedaicourse.com



