Skip to content

Commit 7dd9709

Browse files
committed
Add LSTM training
1 parent fe0ba65 commit 7dd9709

2 files changed

Lines changed: 211 additions & 32 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
import polars as pl
4+
import tensorflow as tf
5+
from data_loading import load
6+
from dataset import FallenDataset
7+
from keras.callbacks import EarlyStopping
8+
from keras.layers import (
9+
BatchNormalization,
10+
Conv1D,
11+
Dense,
12+
Dropout,
13+
Flatten,
14+
InputLayer,
15+
MaxPooling1D,
16+
ReLU,
17+
Reshape,
18+
Softmax,
19+
)
20+
from keras.models import Sequential
21+
from sklearn.metrics import confusion_matrix
22+
from tensorflow import keras
23+
24+
25+
def plot_training_history(history, model_name) -> None:
26+
fig, (ax1, ax2) = plt.subplots(1, 2)
27+
fig.suptitle(f"Model {model_name}")
28+
fig.set_figwidth(15)
29+
30+
ax1.plot(
31+
range(1, len(history.history["accuracy"]) + 1),
32+
history.history["accuracy"],
33+
)
34+
ax1.plot(
35+
range(1, len(history.history["val_accuracy"]) + 1),
36+
history.history["val_accuracy"],
37+
)
38+
ax1.set_title("Model accuracy")
39+
ax1.set(xlabel="epoch", ylabel="accuracy")
40+
ax1.legend(["training", "validation"], loc="best")
41+
42+
ax2.plot(
43+
range(1, len(history.history["loss"]) + 1), history.history["loss"]
44+
)
45+
ax2.plot(
46+
range(1, len(history.history["val_loss"]) + 1),
47+
history.history["val_loss"],
48+
)
49+
ax2.set_title("Model loss")
50+
ax2.set(xlabel="epoch", ylabel="loss")
51+
ax2.legend(["training", "validation"], loc="best")
52+
plt.show()
53+
54+
55+
# Build model
56+
def build_linear_model(num_classes: int, summary: bool = False):
57+
model = Sequential()
58+
59+
# ADD YOUR LAYERS HERE
60+
model.add(
61+
Conv1D(filters=32, kernel_size=32, padding="same", activation="relu")
62+
)
63+
model.add(
64+
Dense(
65+
32,
66+
activation="relu",
67+
activity_regularizer=tf.keras.regularizers.l1(0.00001),
68+
)
69+
)
70+
71+
model.add(Dropout(0.15))
72+
model.add(Dense(num_classes, activation="softmax"))
73+
74+
# Compile model
75+
model.compile(
76+
optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"]
77+
)
78+
79+
if summary:
80+
model.summary()
81+
82+
return model
83+
84+
85+
def train_model(model, x_train, y_train):
86+
early_stopping = EarlyStopping(
87+
monitor="val_loss",
88+
patience=50,
89+
min_delta=0.001,
90+
mode="min",
91+
)
92+
93+
num_epochs = 300
94+
history = model.fit(
95+
x_train,
96+
y_train,
97+
batch_size=128,
98+
epochs=num_epochs,
99+
validation_split=0.2,
100+
callbacks=[early_stopping],
101+
)
102+
plot_training_history(history, 1)
103+
104+
105+
def split_data(input_data, labels):
106+
train_test_split = 0.8
107+
split_index = int(len(input_data) * train_test_split)
108+
109+
x_train = input_data[:split_index]
110+
x_test = input_data[split_index + 1 :]
111+
112+
y_train = labels[:split_index]
113+
y_test = labels[split_index + 1 :]
114+
115+
return (x_train, y_train, x_test, y_test)
116+
117+
118+
def evaluate_model(model, x_test, y_test):
119+
(test_loss, accuracy) = model.evaluate(x_test, y_test)
120+
print("Test accuracy: {}, test loss: {}", accuracy, test_loss)
121+
cm = confusion_matrix(
122+
np.argmax(y_test, axis=1), np.argmax(model.predict(x_test), axis=1)
123+
)
124+
cm = cm.astype("float") / cm.sum(axis=1)[:, np.newaxis]
125+
126+
labels = ["Upright", "Falling", "Fallen"]
127+
import pandas as pd
128+
129+
cm = pd.DataFrame(cm, index=labels, columns=labels)
130+
131+
plt.figure(figsize=(3, 3))
132+
import seaborn as sns
133+
134+
ax = sns.heatmap(
135+
cm * 100,
136+
annot=True,
137+
fmt=".1f",
138+
cmap="Blues",
139+
cbar=False,
140+
)
141+
ax.set_ylabel("True Class", fontdict={"fontweight": "bold"})
142+
ax.set_xlabel("Predicted Class", fontdict={"fontweight": "bold"})
143+
144+
plt.show()
145+
146+
147+
if __name__ == "__main__":
148+
df = load("data.parquet")
149+
dataset = FallenDataset(
150+
df,
151+
group_keys=[pl.col("robot_identifier"), pl.col("match_identifier")],
152+
features=[
153+
pl.col("Control.main_outputs.robot_orientation.pitch"),
154+
pl.col("Control.main_outputs.robot_orientation.roll"),
155+
pl.col("Control.main_outputs.robot_orientation.yaw"),
156+
pl.col("Control.main_outputs.has_ground_contact"),
157+
],
158+
)
159+
dataset.to_windowed(window_size=0.7, window_stride=10 / 83)
160+
161+
model = build_linear_model(dataset.n_classes())
162+
163+
input_data = dataset.get_input_tensor()
164+
data_labels = dataset.get_labels_tensor()
165+
(x_train, y_train, x_test, y_test) = split_data(input_data, data_labels)
166+
167+
y_train = keras.utils.to_categorical(y_train, dataset.n_classes())
168+
y_test = keras.utils.to_categorical(y_test, dataset.n_classes())
169+
170+
train_model(model, x_train, y_train)
171+
172+
evaluate_model(model, x_test, y_test)
173+
174+
converter = tf.lite.TFLiteConverter.from_keras_model(model)
175+
model_tflite = converter.convert()
176+
with open("../../../etc/neural_networks/fall_detection.tflite", "wb") as f:
177+
f.write(model_tflite)

tools/machine-learning/fall_detection/scripts/train_sequential.py

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import os
2-
from datetime import datetime
3-
41
import matplotlib.pyplot as plt
52
import numpy as np
63
import polars as pl
74
import tensorflow as tf
5+
from data_loading import load
6+
from dataset import FallenDataset
87
from keras.callbacks import EarlyStopping
98
from keras.layers import (
109
BatchNormalization,
1110
Conv1D,
11+
LSTM,
1212
Dense,
1313
Dropout,
1414
Flatten,
@@ -19,12 +19,8 @@
1919
Softmax,
2020
)
2121
from keras.models import Sequential
22-
from keras.optimizers.legacy import Adam
23-
from tensorflow import keras
2422
from sklearn.metrics import confusion_matrix
25-
26-
from data_loading import load
27-
from dataset import FallenDataset
23+
from tensorflow import keras
2824

2925

3026
def plot_training_history(history, model_name) -> None:
@@ -58,36 +54,29 @@ def plot_training_history(history, model_name) -> None:
5854

5955

6056
# Build model
61-
def build_model(num_classes: int, summary: bool = True):
57+
def build_sequential_model(
58+
num_features: int,
59+
input_length: int,
60+
num_classes: int,
61+
summary: bool = False,
62+
):
6263
model = Sequential()
6364

6465
# ADD YOUR LAYERS HERE
65-
model.add(Conv1D(3, kernel_size=5, padding="same", activation="relu"))
66-
model.add(MaxPooling1D(pool_size=2, strides=2, padding="same"))
67-
model.add(Dropout(0.25))
68-
model.add(Flatten())
66+
model.add(InputLayer(shape=(num_features, input_length)))
6967
model.add(
70-
Dense(
71-
40,
72-
activation="relu",
73-
activity_regularizer=tf.keras.regularizers.l1(0.00001),
74-
)
75-
)
76-
model.add(
77-
Dense(
78-
20,
79-
activation="relu",
80-
activity_regularizer=tf.keras.regularizers.l1(0.00001),
68+
LSTM(
69+
128,
70+
return_sequences=True,
8171
)
8272
)
73+
model.add(LSTM(64, dropout=0.2))
8374
model.add(
8475
Dense(
85-
10,
76+
32,
8677
activation="relu",
87-
activity_regularizer=tf.keras.regularizers.l1(0.00001),
8878
)
8979
)
90-
model.add(Dropout(0.15))
9180
model.add(Dense(num_classes, activation="softmax"))
9281

9382
# Compile model
@@ -113,7 +102,7 @@ def train_model(model, x_train, y_train):
113102
history = model.fit(
114103
x_train,
115104
y_train,
116-
batch_size=128,
105+
batch_size=256,
117106
epochs=num_epochs,
118107
validation_split=0.2,
119108
callbacks=[early_stopping],
@@ -135,8 +124,8 @@ def split_data(input_data, labels):
135124

136125

137126
def evaluate_model(model, x_test, y_test):
138-
print(model.evaluate(x_test, y_test))
139-
print(model.predict(x_test))
127+
(test_loss, accuracy) = model.evaluate(x_test, y_test)
128+
print("Test accuracy: {}, test loss: {}", accuracy, test_loss)
140129
cm = confusion_matrix(
141130
np.argmax(y_test, axis=1), np.argmax(model.predict(x_test), axis=1)
142131
)
@@ -175,22 +164,35 @@ def evaluate_model(model, x_test, y_test):
175164
pl.col("Control.main_outputs.has_ground_contact"),
176165
],
177166
)
178-
dataset.to_windowed(window_size=0.7, window_stride=10 / 83)
179167

180-
model = build_model(dataset.n_classes())
168+
dataset.to_windowed(window_size=1.0, window_stride=5 / 83)
181169

182170
input_data = dataset.get_input_tensor()
171+
print(input_data.shape)
183172
data_labels = dataset.get_labels_tensor()
173+
model = build_sequential_model(
174+
input_length=input_data.shape[2],
175+
num_features=input_data.shape[1],
176+
num_classes=dataset.n_classes(),
177+
)
184178
(x_train, y_train, x_test, y_test) = split_data(input_data, data_labels)
185179

186180
y_train = keras.utils.to_categorical(y_train, dataset.n_classes())
187181
y_test = keras.utils.to_categorical(y_test, dataset.n_classes())
188182

183+
print(x_train.shape)
184+
189185
train_model(model, x_train, y_train)
190186

191187
evaluate_model(model, x_test, y_test)
192188

193189
converter = tf.lite.TFLiteConverter.from_keras_model(model)
190+
converter._experimental_lower_tensor_list_ops = False
191+
converter.optimizations = [tf.lite.Optimize.DEFAULT]
192+
converter.target_spec.supported_ops = [
193+
tf.lite.OpsSet.TFLITE_BUILTINS, # enable TensorFlow Lite ops.
194+
tf.lite.OpsSet.SELECT_TF_OPS, # enable TensorFlow ops.
195+
]
194196
model_tflite = converter.convert()
195197
with open("../../../etc/neural_networks/fall_detection.tflite", "wb") as f:
196198
f.write(model_tflite)

0 commit comments

Comments
 (0)