【Silicon Labs 开发套件评测】使用tensorflow原型开发手势识别项目
[复制链接]
本帖最后由 北方 于 2021-9-10 16:41 编辑
1、首先需要引用一下资料来源
https://github.com/arduino/ArduinoTensorFlowLiteTutorials/
这个是arduino运用tensorflow的导则,后来看tensorflow也放弃了在lite上的工作,单独分出来tensorflow lite的子项目
https://github.com/tensorflow/tflite-micro
根据这两个资料可以进行掌握如何用这个工具开发
2、首先创建新的工程,感受gesture的智能工具。用原来的tensorflow范例为基准会更省事。
在新的包里安装六轴传感器,
这个是启动SPI接口读取数据的,后面的代码要根据这个API进行初始化,读取和解析。
可以登录网站获取,
https://docs.silabs.com/gecko-platform/3.2/hardware-driver/api/group-icm20648
3、tensorflow的原始数据
3.1 测试原始数据,根据arduino的数据读取如下图。那么这次读取的数据也要归一化到这样类似的数值范围,这样可以直接使用arduino范例库中的model。独自训练还是比较消耗时间的
3.2 然后从arduino的下载包中,找到model.h。经过baking的model数据格式都是一样的,但是如果不用tensorflow的工具,是不能直接编辑和解析这个文件的。
具体的数据格式如下
需要改成model.h,或者直接复制过来。
3.3 对于数据处理是用下面类直接实现
加载model是用下面的语句,在setup()初始化中完成
model = tflite::GetModel(g_model);
HandleOutput()是自定义的事件处理句柄,可以直接在文件中查询。
3.4 按照上面的逻辑可以很快接用tensorflow的标准训练过程完成一个动态识别的移植,具体识别的手势可以参见上面的引用网址。不再解析。
4. 自定义和训练的方法流程
4.1 更多的情况下,大家还是希望自己训练程序和模型,那么还是建议基于上面的代码框架。
具体需要做的,首先是定义模型的输入数据数量和格式,然后是深度学习网络的结构,最后才是准备数据,开始训练。
4.2 准备数据
如上述采集六轴传感器的每次6组数据,读取数据如下
这组数据是把板子固定在手腕上,向左向右滑动,双击,旋转等任意自己希望定义的手势,至少重复10次,每次的数据都读出并保存在csv文件中,如flex.csv.同时需要给每种手势一个数字编码。如1-10直接的自然数。
4.2 进入训练阶段,这个需要熟悉tensorflow的流程,这个过程在需要科学学习的免费colab上的notebook文件https://colab.research.google.com/github/arduino/ArduinoTensorFlowLiteTutorials/blob/master/GestureToEmoji/arduino_tinyml_workshop.ipynb
为了便于学习全程粘贴如下,很长,但是值得读一下
不过其中最重要的模型一共15行代码,做成隐藏模式,只能直接使用。不能 编辑。这个就需要粉一下百度了,百度的paddlepaddle也做了一个类似的免费网络人工智能训练平台,很烧钱的,大家今早去免费薅羊毛。现在谷歌已经不能免费提供大家这样的服务了,百度估计也坚持不了多久,大家尽早去。
<a href="https://www.arduino.cc/"><img src="https://raw.githubusercontent.com/sandeepmistry/aimldevfest-workshop-2019/master/images/Arduino_logo_R_highquality.png" width=200/></a>
# Tiny ML on Arduino
## Gesture recognition tutorial
* Sandeep Mistry - Arduino
* Don Coleman - Chariot Solutions
https://github.com/arduino/ArduinoTensorFlowLiteTutorials/
## Setup Python Environment
The next cell sets up the dependencies in required for the notebook, run it.
# Setup environment
!apt-get -qq install xxd
!pip install pandas numpy matplotlib
!pip install tensorflow==2.0.0-rc1
# Upload Data
1. Open the panel on the left side of Colab by clicking on the __>__
1. Select the files tab
1. Drag `punch.csv` and `flex.csv` files from your computer to the tab to upload them into colab.
# Graph Data (optional)
We'll graph the input files on two separate graphs, acceleration and gyroscope, as each data set has different units and scale.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
filename = "punch.csv"
df = pd.read_csv("/content/" + filename)
index = range(1, len(df['aX']) + 1)
plt.rcParams["figure.figsize"] = (20,10)
plt.plot(index, df['aX'], 'g.', label='x', linestyle='solid', marker=',')
plt.plot(index, df['aY'], 'b.', label='y', linestyle='solid', marker=',')
plt.plot(index, df['aZ'], 'r.', label='z', linestyle='solid', marker=',')
plt.title("Acceleration")
plt.xlabel("Sample #")
plt.ylabel("Acceleration (G)")
plt.legend()
plt.show()
plt.plot(index, df['gX'], 'g.', label='x', linestyle='solid', marker=',')
plt.plot(index, df['gY'], 'b.', label='y', linestyle='solid', marker=',')
plt.plot(index, df['gZ'], 'r.', label='z', linestyle='solid', marker=',')
plt.title("Gyroscope")
plt.xlabel("Sample #")
plt.ylabel("Gyroscope (deg/sec)")
plt.legend()
plt.show()
# Train Neural Network
## Parse and prepare the data
The next cell parses the csv files and transforms them to a format that will be used to train the fully connected neural network.
Update the `GESTURES` list with the gesture data you've collected in `.csv` format.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
print(f"TensorFlow version = {tf.__version__}\n")
# Set a fixed random seed value, for reproducibility, this will allow us to get
# the same random numbers each time the notebook is run
SEED = 1337
np.random.seed(SEED)
tf.random.set_seed(SEED)
# the list of gestures that data is available for
GESTURES = [
"punch",
"flex",
]
SAMPLES_PER_GESTURE = 119
NUM_GESTURES = len(GESTURES)
# create a one-hot encoded matrix that is used in the output
ONE_HOT_ENCODED_GESTURES = np.eye(NUM_GESTURES)
inputs = []
outputs = []
# read each csv file and push an input and output
for gesture_index in range(NUM_GESTURES):
gesture = GESTURES[gesture_index]
print(f"Processing index {gesture_index} for gesture '{gesture}'.")
output = ONE_HOT_ENCODED_GESTURES[gesture_index]
df = pd.read_csv("/content/" + gesture + ".csv")
# calculate the number of gesture recordings in the file
num_recordings = int(df.shape[0] / SAMPLES_PER_GESTURE)
print(f"\tThere are {num_recordings} recordings of the {gesture} gesture.")
for i in range(num_recordings):
tensor = []
for j in range(SAMPLES_PER_GESTURE):
index = i * SAMPLES_PER_GESTURE + j
# normalize the input data, between 0 to 1:
# - acceleration is between: -4 to +4
# - gyroscope is between: -2000 to +2000
tensor += [
(df['aX'][index] + 4) / 8,
(df['aY'][index] + 4) / 8,
(df['aZ'][index] + 4) / 8,
(df['gX'][index] + 2000) / 4000,
(df['gY'][index] + 2000) / 4000,
(df['gZ'][index] + 2000) / 4000
]
inputs.append(tensor)
outputs.append(output)
# convert the list to numpy array
inputs = np.array(inputs)
outputs = np.array(outputs)
print("Data set parsing and preparation complete.")
## Randomize and split the input and output pairs for training
Randomly split input and output pairs into sets of data: 60% for training, 20% for validation, and 20% for testing.
- the training set is used to train the model
- the validation set is used to measure how well the model is performing during training
- the testing set is used to test the model after training
# Randomize the order of the inputs, so they can be evenly distributed for training, testing, and validation
# https://stackoverflow.com/a/37710486/2020087
num_inputs = len(inputs)
randomize = np.arange(num_inputs)
np.random.shuffle(randomize)
# Swap the consecutive indexes (0, 1, 2, etc) with the randomized indexes
inputs = inputs[randomize]
outputs = outputs[randomize]
# Split the recordings (group of samples) into three sets: training, testing and validation
TRAIN_SPLIT = int(0.6 * num_inputs)
TEST_SPLIT = int(0.2 * num_inputs + TRAIN_SPLIT)
inputs_train, inputs_test, inputs_validate = np.split(inputs, [TRAIN_SPLIT, TEST_SPLIT])
outputs_train, outputs_test, outputs_validate = np.split(outputs, [TRAIN_SPLIT, TEST_SPLIT])
print("Data set randomization and splitting complete.")
## Build & Train the Model
Build and train a [TensorFlow](https://www.tensorflow.org) model using the high-level [Keras](https://www.tensorflow.org/guide/keras) API.
# build the model and train it
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(50, activation='relu')) # relu is used for performance
model.add(tf.keras.layers.Dense(15, activation='relu'))
model.add(tf.keras.layers.Dense(NUM_GESTURES, activation='softmax')) # softmax is used, because we only expect one gesture to occur per input
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
history = model.fit(inputs_train, outputs_train, epochs=600, batch_size=1, validation_data=(inputs_validate, outputs_validate))
## Verify
Graph the models performance vs validation.
### Graph the loss
Graph the loss to see when the model stops improving.
# increase the size of the graphs. The default size is (6,4).
plt.rcParams["figure.figsize"] = (20,10)
# graph the loss, the model above is configure to use "mean squared error" as the loss function
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'g.', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
print(plt.rcParams["figure.figsize"])
### Graph the loss again, skipping a bit of the start
We'll graph the same data as the previous code cell, but start at index 100 so we can further zoom in once the model starts to converge.
# graph the loss again skipping a bit of the start
SKIP = 100
plt.plot(epochs[SKIP:], loss[SKIP:], 'g.', label='Training loss')
plt.plot(epochs[SKIP:], val_loss[SKIP:], 'b.', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
### Graph the mean absolute error
[Mean absolute error](https://en.wikipedia.org/wiki/Mean_absolute_error) is another metric to judge the performance of the model.
# graph of mean absolute error
mae = history.history['mae']
val_mae = history.history['val_mae']
plt.plot(epochs[SKIP:], mae[SKIP:], 'g.', label='Training MAE')
plt.plot(epochs[SKIP:], val_mae[SKIP:], 'b.', label='Validation MAE')
plt.title('Training and validation mean absolute error')
plt.xlabel('Epochs')
plt.ylabel('MAE')
plt.legend()
plt.show()
### Run with Test Data
Put our test data into the model and plot the predictions
# use the model to predict the test inputs
predictions = model.predict(inputs_test)
# print the predictions and the expected ouputs
print("predictions =\n", np.round(predictions, decimals=3))
print("actual =\n", outputs_test)
# Plot the predictions along with to the test data
plt.clf()
plt.title('Training data predicted vs actual values')
plt.plot(inputs_test, outputs_test, 'b.', label='Actual')
plt.plot(inputs_test, predictions, 'r.', label='Predicted')
plt.show()
# Convert the Trained Model to Tensor Flow Lite
The next cell converts the model to TFlite format. The size in bytes of the model is also printed out.
# Convert the model to the TensorFlow Lite format without quantization
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
# Save the model to disk
open("gesture_model.tflite", "wb").write(tflite_model)
import os
basic_model_size = os.path.getsize("gesture_model.tflite")
print("Model is %d bytes" % basic_model_size)
## Encode the Model in an Arduino Header File
The next cell creates a constant byte array that contains the TFlite model. Import it as a tab with the sketch below.
!echo "const unsigned char model[] = {" > /content/model.h
!cat gesture_model.tflite | xxd -i >> /content/model.h
!echo "};" >> /content/model.h
import os
model_h_size = os.path.getsize("model.h")
print(f"Header file, model.h, is {model_h_size:,} bytes.")
print("\nOpen the side panel (refresh if needed). Double click model.h to download the file.")
# Classifying IMU Data
Now it's time to switch back to the tutorial instructions and run our new model on the Arduino Nano 33 BLE Sense to classify the accelerometer and gyroscope data.
4.3 训练后的就生产了model.h文件,这样就可以如上一节一样来用自定义的模型实现自定义的手势。上面的代码就是一个用手势替代emojo键盘的方式。
需要注意的是,这个识别的准确率并不高,有识别错误的概率。一个是训练的数据不够多,另一个是经过压缩的模型有损失了很多的精度。不过用于非严格的方案,还是一个神奇的方法。
在arduino的范例里,还有一个简单的语音识别模型。这个模型,识别的是up,down等英语上下单词,可以用来控制小车。不过,普遍反映,这个模型的识别效果不那么好。
|