SVMを使用して筋電波形から上肢の動作推定システムを実装する

イントロ

本記事では、PythonとJupyter notebookを使用して、8動作の筋電波形(EMG)のデータセット機械学習モデルに学習させます。
そして、入力された筋電波形が8動作のどの動作に属するかを予測するシステムを実装しながら機械学習の使い方について理解していきます。

簡単に実装できるように、本記事で記載されるコードはjupyterのセルに順に貼り付けて実行できるようになっています。

また、私のコードの書き方が下手でところどころわかりにくいところがあるかもしれませんので、ご了承ください。

機械学習のモデル実装の流れ

機械学習を行うための準備から、その精度評価までの大まかな流れ

  1. データの読み込み
  2. 読み込んだデータで学習が行えるように前処理をして学習データを生成
  3. 処理した学習データからモデルの学習
  4. 学習データによるモデルの精度評価
  5. 未知データに対する精度評価

上記の流れで今回は実装していきます。

実装していきましょう

今回使用するデータセットは以下からダウンロードできます。
data.mendeley.com

ダウンロード方法は "Download all files(8)" と書かれたオレンジのボタンを押せば、zip形式でダウンロードできます。
f:id:Yunos:20200501151818p:plain

ダウンロードできたファイルを解凍して、"8hand_gesture_dataset" と名前を変更して、今回のシステムを実装するファイルと同じディレクトリ内においてください。

1. 前準備

今回は以下のライブラリが必要になるので、入れていない人はpip installしておいてください。

  • numpy
  • sklearn


ここでは必要なライブラリのインポートとデータ取得用の関数を定義する。

# 1.前準備
# データセットのロード

import numpy as np

# 各ジェスチャのファイルパス
path1 = "8hand_gesture_dataset/HandGesture01.txt"
path2 = "8hand_gesture_dataset/HandGesture02.txt"
path3 = "8hand_gesture_dataset/HandGesture03.txt"
path4 = "8hand_gesture_dataset/HandGesture04.txt"
path5 = "8hand_gesture_dataset/HandGesture05.txt"
path6 = "8hand_gesture_dataset/HandGesture06.txt"
path7 = "8hand_gesture_dataset/HandGesture07.txt"
path8 = "8hand_gesture_dataset/HandGesture08.txt"

path = [path1, path2, path3, path4, path5, path6, path7, path8]

# 取得したいジェスチャのデータをファイルパスとデータ番号から筋電データを取得する
def path_to_data(path, rand_num): 
    # 8hand_gesture_dataset内のテキストファイルの読み込み
    with open(path) as f:
        s = f.read()

    # テキストファイルの内容は全て文字列型なので、Numpy配列の機械学習しやすいように変換する
    s_l = s.split('\n\n')
    del s_l[0]

    all_data = np.array([])   
    k = 0 #1周目のループを判断するためのブール値 
    
    # 分割した50データはまだ文字列型の配列でまとめられているから、機械学習がしやすいように
    # データを変換して、int型の配列に変換
    # rand_numにリスト型で指定した値で取得するデータを指定
    for num in rand_num:
        # 1データをセンサ数で分割
        data = s_l[num].split("\n")
        del data[0]

        # サンプルを8センサで読み取った、1センサあたり100サンプルの8×100のint型配列に変換
        sensor8 = []
        for i in range(len(data)):
            data[i] = data[i].replace('{', '').replace('}', '').replace(' ', '')
            data_str = data[i].split(",")
            for j in range(len(data_str)):
                if data_str[j] == '':
                    del data_str[j]
            data_int = [int(s) for s in data_str]
            sensor8.append(data_int)
        
        # 8×100を100×8に転置
        arr_sensor8 = np.array(sensor8).T
          
        if len(arr_sensor8) == 100:
            if k != 0:
                all_data = np.concatenate([all_data, arr_sensor8], 0)
            elif k == 0:
                all_data = arr_sensor8     
                k = 1
        
    # 変換したデータを連結した配列を返す
    return all_data

2. データの読み込みと特徴量の整形

ここでは1で定義したpath_to_data関数を呼び出して、特徴量配列とクラス配列を取得します。

# 2.データの読み込み
import random

gesture = 1 # 動作のクラス値の変数
all_data = [] # 全ての動作データの保存配列
rand_num_ls = [] # ランダムで決定した学習用のデータの番号の保存配列

for p in path:
    print(p)
    # 50データ中2データをランダムで決定
    rand_num = random.sample(range(50), k = 2)
    # データの取得
    data = path_to_data(p, rand_num)
    
    print(gesture)
    
    # 全ての特徴データとクラスデータを1つの配列にまとめる処理
    # ループ1周目の処理
    if p == path1:
        all_data = data
        gesture_array = np.full(len(data), gesture)
    # 1周目以降の処理
    else:
        all_data = np.concatenate([all_data, data])
        gesture_array = np.concatenate([gesture_array, np.full(len(data), gesture)])
    rand_num_ls.append(rand_num)
    
    gesture += 1  
    
print(len(all_data))

# emgの特徴量[sensor1, sensor2, ... , sensor8]
emg_data = all_data

3. データの標準化

読み込んだデータを学習データと評価データに分割して、学習データの分散と平均から標準化します。

# 3データの標準化

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 学習データと評価データに3:7に分割
x_train, x_test, y_train, y_test = train_test_split(emg_data, gesture_array, test_size = 0.3, random_state = None)

# データの標準化処理
sc = StandardScaler()

# x_trainの平均と分散を計算
sc.fit(x_train)

# trainデータとtestデータをfit関数で計算した平均と分散で標準化
x_train_std = sc.transform(x_train)
x_test_std = sc.transform(x_test)

4. 学習データから識別器の学習

学習データから識別器を作成します。

# 4. 学習データから識別器の作成

from sklearn.svm import SVC

# 線形SVCのインスタンス生成
model = SVC(kernel="rbf", random_state=None)

# 識別器の作成
model.fit(x_train_std, y_train)

5. 学習データから識別器の精度評価

学習データを識別器で、どのクラスに属するかを分類した結果と正解の結果を比較して正解率で精度を評価します。

# 5.モデルの精度評価 学習データ
from sklearn.metrics import accuracy_score

# トレーニングデータの特徴量をクラス分類
pred_train = model.predict(x_train_std)
# 予測精度を算出
accuracy_train = accuracy_score(y_train, pred_train)

print('トレーニングデータに対する正解率 : %.2f' % accuracy_train)

筆者の正解率は99%でした。

6. テストデータに対する精度評価

評価データに対しても5と同じ処理をします。

# テストデータに対する精度 
from sklearn.metrics import accuracy_score

# テストデータの特徴量をクラス分類
pred_test = model.predict(x_test_std)

# 予測精度を算出
accuracy_test = accuracy_score(y_test, pred_test)

print('トレーニングデータに対する正解率 : %.2f' % accuracy_test)

筆者の正解率は99%でした。

7. 学習に使用したデータ以外の残りの未知データの読み込み

8動作のデータセット内にはそれぞれ50データあり、そのうち各動作の2データ、つまり16データを使用して識別器を作成しました。
このセルでは、識別器に使用したデータ以外の48×8データをpath_to_data関数を使用して読み込みます。

# 7. 学習に使用したデータ以外のデータを機械学習用に変換
gesture = 1
for p in path:
    print(p)
    print(gesture)
    
    data_index = list(range(50))
    
    # 学習に使用したデータ番号の削除
    for num in rand_num_ls[gesture-1]:  
        data_index.remove(num)
        
    data = path_to_data(p, data_index) # 学習に使用したデータ以外のデータを取得
    
    # 全ての特徴データとクラスデータを一つの配列にまとめる
    # 1周目の処理
    if p == path1:
        all_data = data
        gesture_array = np.full(len(data), gesture)
    # 1周目以降の処理
    else:
        all_data = np.concatenate([all_data, data])
        gesture_array = np.concatenate([gesture_array, np.full(len(data), gesture)])
    
    print(len(all_data))
    
    gesture += 1
    
# 学習に使用したデータ以外の特徴データを全てまとめた配列
ex_data = all_data
print(len(gesture_array))
# 学習に使用したデータ以外のクラスデータを全てまとめた配列
ex_gesture_array = gesture_array

8. 未知データのクラス分類と精度評価

5、6と同じ処理を行って精度を評価します。

# 8.未知データでの評価

#ex_dataの標準化
ex_data_std = sc.transform(ex_data)

# ex_data_stdの各特徴データのクラスを予測
pred_ex_data = model.predict(ex_data_std)

# 予測精度を計算
print(accuracy_score(pred_ex_data, ex_gesture_array))

筆者の正解率は94%でした。



いかがだったでしょうか。

今後さらに、交差検証や他の前処理を加える事で精度の向上をはかっていきたい。

筆者のこのシステムを作った目的と背景

筋電義手開発のために大学の研究で筋電波形の動作推定システムに関する研究をしています。
そのため、複数パターンの動作を識別できる機械学習に魅力を感じており、その使い方を理解するための第一歩としてこのシステムを作りました。
また、最近はMyoアームバンドという筋電計測器を使用しているので、この計測器によって計測されたEMGデータにどのような処理をすればSVMが使えるかを実験考察するために作りました。