【Python】IMUのScikit-Learn SVM分類ミスの解析<Pgmと論文学習>

SVM決定境界線プロットとSVMのAccuracyをみると、元データ(教師となるトレーニングデータ)を何にするかが原因であったことが判りました。フタをあければ、当たり前ですが、XYZの加速度成分を分類するのに、XYZが同時に動く振り方(ジェスチャー)をしているのでそれぞれのジェスチャー内に重複するデータが多数発生するのが、分類ミスが多発する原因でした。
●元のトレーニングデータ(教師となるデータ)
BNO055のLinearAcc(laX,laY,laZ)を採用したのですが、Linearだと重力加速度は除外しているので、姿勢差がでません。下記生データ時系列図では、左からX方向往復振り、中央がY方向往復振り、右がZ方向往復振りです。
それぞれの方向の波形が大きなピークをだしてますが、低い値では他の成分も加速が入ってしまってます。
ですので、テストデータが低い値の場合は、どの振り回し方だか分類がつかないでミスが発生します。


●機械学習の評価指標でみると
SVM分類のトレーニングデータの正解率(Accuracy)69.4%しかでてませんでした。
F1値(F値は対照的な特徴を持つ適合率と再現率の調和平均)が69.9%でした

●境界線グラフ(seaborn-analyzer使用
左図:縦laY-横laX 中図:縦laZ-横laY   右図:縦laX-laZ
入り混じっているので正解率が69%なのは納得です。

●元々の原作者の意図

魔法の杖のジェスチャーを機械学習で判定させてみ
の作者の振り回し方は
姿勢を変えながらトレーニングデータをサンプリングさせていたのだと思います。
ですので、私がLinear加速度を使ったら、姿勢がでないので、SVMで分類できないトレーニングデータであっ
ということです。 Gravity加速度で、与えるか一般の加速度でトレーニングしたほうが、正解率はあがるはずです。
●IMUを使った機械学習の論文調査
剣道の技をIMUを複数個身体にとりつけて、つき技を分類した論文がありました。
剣道上達支援のためのIMUを用いた打突動作 
ここでは、技毎に統計値を目いっぱいとって、機械学習のアルゴリズムを試してますが、一番良い正解率が
Randum Forestで94%でした。特徴量として、統計値 時間、周波数でとってます。
この論文の引用文献でさかのぼって、人間の行動をスマホIMUで判ありました。
有料と無料のダウンロードがありますが、無料でもダウンロードできます。
Multi-stage activity inference for locomotion and transportation analytics of mobile users
ここで使っているデータと特徴量のテーブルだけ抜粋しました。

結果として 下記のツリーで分類ができるようです。

●私のやりたいこと
時系列グラフだけみて、どんな動作をしているかは、人間の目でも難しいので、上記論文のやり方を学習して
IMUの新しい使い方をやってみたいと思います。
●プログラムの備忘録
元プログラムは、IMUからのデータをリストにアペンドしながらトレーニングデータを作成してますが、
csvファイルから読みとって scikit-learn用にデータを変換する方法でプログラムを作り直しました。

#coding utf-8
import datetime
import pickle  # モデルをファイルとして使用する用
import keyboard
import numpy as np  # numpyで2次元配列を扱うため

import serial # シリアル通信を行うため
from sklearn.svm import SVC, SVR  # 学習用
import csv
import os
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import time
#----------Global variable----------------

X = []
Y = []

def  serTch(filenamei,length:int,gestures:int,cmN):
        #X = []
        #Y = []
        wr=[]
        print("serTch INN")
        path = os.getcwd()
        print(path)
        print(type(path))
        #length = 100 #ジェスチャの長さ(連続で受信するデータの個数)
        #gestures = 3 #ジェスチャの種類の数
        times = 1 #各ジェスチャを行う回数
        #====================================================
       #now = datetime.datetime.now()
        #name = now.strftime('%m%d_%H%M')
        #filenamei ='imu.csv'
        #filename = 'model_' + name + '.sav'  # 入力した名前の学習モデルのファイル名
        #filename = 'model.sav'  # 入力した名前の学習モデルのファイル名
        print('Connecting...')
        ser = serial.Serial(cmN,115200,timeout=None) #ポート番号19からシリアル受信
        print("-----Input  c:CreateModel,g:Gesture Measuring +ENTER")
        print("filenamei="+filenamei+"length="+str(length)+"gestures="+str(gestures))
        #mx = []  # 各軸ごとの配列を初期化
        #my = []
        #mz = []
        with open(filenamei, 'w', newline='') as f:
                        writer = csv.writer(f)
                        for i in range(gestures * times):
                                cnt = 0 #受信したデータのカウント用
                                gesture_num = i  % gestures + 1 #ジェスチャ番号を指定
                                print('Push a then Do gesture No.', gesture_num)
                                while True:
                                    line=ser.readline()#Binary
                                    linestr= line.decode('utf8')#バイナリーをASCII変換
                                    strarr=linestr.split(',')
                                    datano=strarr[0]
                                    x=strarr[1]
                                    y=strarr[2]
                                    z=strarr[3]
                                    tim=strarr[4]

                                    if keyboard.is_pressed('a'):
                                        print("key hit")
                                        switch = "a"
                                    else:
                                        switch=''

                                    if switch =='a' and cnt == 0: #クリックされたらカウント開始
                                        print("cnt=1")
                                        cnt = 1

                                    if cnt != 0: #ジェスチャ中a
                                        #print("In gestureNo"+str(gesture_num)+" datacnt="+str(cnt))

                                        print("In gestureNo"+str(gesture_num)+'datanp=' + datano + ',x=' + x + 'y=' + y+ 'z=' + z + 'tim=' + tim)
                                        #mx.append(x.rstrip('\r\n'))  # シリアルから受け取ってたデータのゴミを取って配列へ追加
                                        #my.append(y.rstrip('\r\n'))
                                        #mz.append(z.rstrip('\r\n'))
                                        cnt += 1
                                        if cnt == length: #lengthまでいったら次のジェスチャ待ちへ
                                            print("length Up")
                                            break
                                        print("CSV:gnum="+str(gesture_num)+"x="+x+"y="+y+"z="+z)
                                        wr = [gesture_num,x, y, z]
                                        writer.writerow(wr)
                                #Y.append(str(gesture_num))  # 学習ラベル用リストへ追加
                                #m = mx + my + mz #三軸の加速度を横一列にする
                                #X.append(m) #学習データ用リストへ追加
                                #print("Xappend:Gesture_num="+str(gesture_num))
                                #print(X)


                        print("serial closed")
                        ser.close()  # シリアルを閉じ
        f.close()  # isn't actually needed
                        #print("def result:")
                        #print(X)
                       # print(Y)
        #return  [X,Y]

# -----------main---------------------------------------
while True:
            print("-----Input  c:CreateModel,g:Gesture Measuring +ENTER")
            keys = input()
            millisec = time.time() * 1000
            if keys == 'c':
                serTch('imu.csv',100,3,'COM28')
                #X=result[0]
                #X=X[0]
                #Y=result[1]
                # CSVからデータ作成
                #with open('imu.csv', 'w', newline='') as f:
                df = pd.read_csv("imu.csv", sep=",", header=None, names=['gnum', 'lx', 'ly', 'lz'])
                print(df)
                # データフレームを行列へ変換
                arr = df.to_numpy()
                print(arr)
                X=arr[:,1:]
                Y=arr[:,0]
                print("---X=",end='')
                print(X)
                print("---Y=",end='')
                print(Y)
                #X = np.array(X).reshape(-1, 1)
                print("X=",end='')
                print(X)

                # SVM start
                #X = np.array(X).reshape(-1, 1)
                model = SVC(kernel = 'linear', C=1, gamma=1) #学習モデルのパラメータを指定
                #model.fit(X,np.ravel(Y)) #学習を行う
                model.fit(X, Y)  # 学習を行う
                # %% サポートベクターマシン分類して性能評価指標算出
                from sklearn.svm import SVC
                from sklearn.metrics import accuracy_score, f1_score

                # X = iris[['petal_width', 'petal_length']].values  # 説明変数
                # y = iris['species'].values  # 目的変数(3種類のアヤメ種類)
                #clf = SVC()  # SVM分類用インスタンス
                #clf.fit(X, Y)  # 学習
                y_pred = model.predict(X)  # 推論
                print(f'Accuracy={accuracy_score(Y, y_pred)}')  # 正解率を表示
                print(f'F1={f1_score(Y, y_pred, average="macro")}')  # F1-Macroを表示
                from seaborn_analyzer import classplot

                classplot.class_separator_plot(model, ['lx','ly'],'gnum', df)
                classplot.class_separator_plot(model, ['ly', 'lz'], 'gnum', df)
                classplot.class_separator_plot(model, ['lz', 'lx'], 'gnum', df)
                plt.show()
                # CSV読み込みして散布図プロット
                #df = pd.read_csv("imu.csv", sep=",", header=None, names=['gnum', 'gyX', 'gyY', 'gyZ'])

                #df = pd.read_csv("imu.csv", sep=",", header=None, names=['gnum', 'acX', 'acY', 'acZ'])
                print(df)
                print(len(df.columns))
                print(df.info())

                sns.pairplot(df, hue='gnum')
                plt.tight_layout()
                plt.show()
                sns.lineplot(df)
                plt.show()
                #df = pd.DataFrame(model)
                #sns.pairplot(df, height=1.5)
                with open('model.sav', 'wb') as fp_model: #学習モデルを保存するためのファイルを開く
                    pickle.dump(model, fp_model) #モデルを保存
                print('Model created.')

             #-----------------------------start gesture measuring-----------------------------------------------
            elif keys=='g':
                print("=================Test Input MODE================ ")
                serTch('imut.csv', 10, 1, 'COM28')

                with open('model.sav', 'rb') as fp_model:  # 学習モデルファイルを開く
                        loaded_model = pickle.load(fp_model)  # モデルをロード
                        print('Do any gesture!')
                        with open('imut.csv', 'rb') as fs:
                            writer = csv.writer(fs)
                        dfs = pd.read_csv("imut.csv", sep=",", header=None, names=['gnum', 'lx', 'ly', 'lz'])
                        print(dfs)
                        arr2=dfs.to_numpy()
                        print("arr2=", end='')
                        print(arr2)
                        test_arr=arr2[:,1:]
                        print("test_arr=",end='')
                        print(test_arr)
                        pre = loaded_model.predict(test_arr)  # ジェスチャの判定
                        print('********************This gesture is No.', pre)
                        sns.lineplot(dfs)
                        plt.show()




                        plt.show()


①IMUサンプリング
IMUのサンプリング関数でジェスチャー毎にサンプリングして、CSVファイルに保存する。
gnum,lax,lay,laz  の4列を1行とするCSVファイルgnumがジェスチャー番号で
3つのジェスチャーを一つのCSVファイルimu.csvに収納します。

1 -8.79 5.02 -3.01
1 3.13 4.37 -1.12
1 13.53 3.21 -0.52
1 33.54 -4.75 4.99
1 35.86 -10.85 4.63
1 16.28 0.78 0.22
1 1.61 6.81 5.1
1 -22.05 12.4 1.87

②scikit-leanトレーニングデータへのCSVデータ変換
scikit-learnは、pandasのデータフレームは受け入れてくれませんので、
行列フォーマットに変換しないといけません。
arr = df.to_numpy() #ここで、pandas DataFrameからnumpyの行列データ arrに変換しました。

CSVからpandas DataFrame dfへ  変換  numpy の行列データarr
gnum lx ly lz
0 1 -2.39 -3.48 -1.57
1 1 -6.91 -2.71 -1.95
2 1 -28.57 2.07 -4.87
3 1 -27.46 3.36 -3.93
4 1 -22.68 1.74 -3.15
df.to_numpy() [[ 1. -2.39 -3.48 -1.57]
[ 1. -6.91 -2.71 -1.95]
[ 1. -28.57 2.07 -4.87]

[ 3. 5.08 -1.25 6.16]
[ 3. 3.02 -0.98 2.22]
[ 3. 1.61 -0.44 -7.39]]

データ行列とラベル行列にarrをスライスします。
X=arr[:,1:]

X=[[-2.390e+00 -3.480e+00 -1.570e+00]
[-6.910e+00 -2.710e+00 -1.950e+00]
[-2.857e+01 2.070e+00 -4.870e+00]
[-2.746e+01 3.360e+00 -3.930e+00]
[-2.268e+01 1.740e+00 -3.150e+00]
[-9.850e+00 -1.540e+00 -6.100e-01]

Y=arr[:,0]

Y=[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
2. 2. 2. 2. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3.
3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3.
3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3.
3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3.
3. 3. 3. 3. 3. 3.]

●トレーニングデータX YをSVMにいれてFITTING
model = SVC(kernel = ‘linear’, C=1, gamma=1) #学習モデルのパラメータを指定
#model.fit(X,np.ravel(Y)) #学習を行う
model.fit(X, Y) # 学習を行う

●感想
判らなかったのは、トレーニングデータの加工方法で、WEB上では、既成データセットを使っている事例が多く
一からデータを作るやり方が少ないので、わからないままいたので、SVMのデータ入力エラーが多発しました。
要するに、scikit-leranは、行列データしか受け入れてくれないので、行列データをnumpyで作成する以外に方法が
無いということがどこにも書いてなくて、さまよい続けました。ここのサイト様に感謝です。

CSVファイルをpandasで読み込み、scikit-learnで学習させる
https://kakedashi-engineer.appspot.com/2020/03/21/pandas-sklearn/?utm_source=pocket_saves

●以後
IMUのlinear加速度でのピーク判別視点で、トレーニングデータを作成して、学習させてみます。
正解率98%以上でないと、機械学習の効果が感じられないので、粘ってみます。



 

 

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です