create-react-appで作成したReactアプリでFirebaseを使えるようにする

create-react-appで作成したReactアプリをgithubにアプロードし、Firebaseでウェブ上に公開する方法を記述する。

最初にGitHubのページで"New Repository"から新しくリポジトリを作成する。埋めるのはRepository nameのみでいい。

次にコマンドラインからReactアプリを作成していく。

Reactアプリの作成

$ npx create-react-app ディレクトリ名

作成されたReactアプリのディレクトリに移動

$ cd ディレクトリ名

GitHub上への作成したReactアプリディレクトリのアップロード。create-react-appで既にGitリポジトリが存在するため"$ git init"は必要ない。

ディレクトリ名 $ git remote add origin https://github.com/your-name/project-name.git
ディレクトリ名 $ git push -u origin master

これでGitHubへのアップロードが完了。

"npm start"でローカルでReactアプリを起動。"localhost"でアプリが起動して、以下のページが表示されていればOK。
f:id:Yunos:20200903182046p:plain


次にFirebaseを使えるようにしていく。
Firebaseにアクセスして、"使ってみる"を選択し"プロジェクトを追加"でプロジェクトを作成します。
プロジェクト名を入力します。アナリスティクスはとりあえずオフでOK。
以下のメイン画面が表示されたらプロジェクトの作成完了。
f:id:Yunos:20200903184426p:plain

メイン画面のボタンを押してアプリの登録をします。アプリ名は何でもOK。Hostingは後で設定するのでチェックせずで大丈夫。

Cloud Firestoreを有効化して、データベースを使用できるようにします。Cloud Firestoreを開き、データベースを作成を押します。モードはテストモード。ロケーションはasia-southeast1。

作成したReactアプリのディレクトリの中の「src」ディレクトリに「plugins」ディレクトリを作成し、「plugins」の中に「firebase.js」というjsファイルを作成してください。
そしたら、FirebaseのProject overviewの歯車マークから「プロジェクトを設定」を開き下にスクロールして以下のコードをfirebase.jsにコピペしてください。

import firebase from 'firebase';

const config = {
//ここから
    apiKey: "****************************",
    authDomain: "****************************",
    databaseURL: "****************************",
    projectId: "****************************",
    storageBucket: "****************************",
    messagingSenderId: "****************************",
    appId: "****************************",
//ここまでコピーして書き換え
  };
const firebaseApp = firebase.initializeApp(config);
export const firestore = firebaseApp.firestore();

ローカル環境でFirebaseを扱えるようにするため、FirebaseCLIをglobalでインストールします(1回きり)。

$ npm install -g firebase-tools

CLIが導入できたら

$ firebase login

次に初期化を行う

$ firebase init

これを実行すると選択項目が出てくる。
ここでは、方向キーで指定して、スペースキーで選択、enterで決定ができる。
以下を順に選択していきます。

Hostings
↓
Use an existing project (作成したプロジェクトを選択)
↓
What do you want to use as your public directory?(ディレクトリを選択します。buildと入力しておきましょう)
↓
あとはyesでOK

最後にビルドしてデプロイする。

$ npm run build
$ firebase deploy

表示されたURLで以下の画像が表示されたらOK
f:id:Yunos:20200903182046p:plain


これでセットアップ完了

XcodeでMyo.frameworkを使用するためのセットアップ方法

Xcodeのcommand line toolを使用して、Myoアームバンドに接続するアプリケーションを作成するためのセットアップ方法

手順

1. Welcome to XcodeウィンドウからCreate a new Xcode projectを選択しましす。テンプレートはCommand Line Toolを選択して、言語はC++でプロジェクトを作成します。

2. プロジェクトが作成できたら、左側のプロジェクトナビゲーターのファイルツリー内の一番上のルートノードを選択します。プロジェクトのプロパティウィンドウが表示されます。

f:id:Yunos:20200516094654p:plain
ファイルツリー内のルートノードを選択

3. プロパティウィンドウが表示されたら、上部タブの"Build Settings"を選択します。次に検索タブから"Runpath Search Paths"を検索して、「@loader_path」を設定します。

f:id:Yunos:20200516095652p:plain
プロパティウィンドウの"Build Settings"を選択

4. プロジェクトナビゲーターの左下にある"+"をクリックして表示されたメニューから"New Group"をクリックして、ワークスペース内にグループを作成し、グループ名を"Frameworks"にします。

f:id:Yunos:20200516132533p:plain
ファイルツリー内に"Frameworks"グループを作成

5. 作成した"Frameworks"グループを右クリックして、メニューを開いて"Show in Finder"でディレクトリを表示します。開いたディレクトリの中に、"myo.framework"をコピペします。

f:id:Yunos:20200516133417p:plain
Frameworksディレクトリにmyo.frameworkをコピペ

6. プロパティウィンドウに戻って、上部タブの"Build Phases"を選択します。Link Binary With Librariesの"+"を押して、5でFrameworksディレクトリに追加したmyo.frameworksを参照して開きます。

f:id:Yunos:20200516134206p:plain
Link Binary With Librariesにmyo.frameworksを追加

7. 次にCopy Filesの"+"を押して、Frameworks内のmyo.frameworkを追加します。Destinationを"Product Directory"に設定して、subpathを消して、Copy only when installingのチェックを外します。

f:id:Yunos:20200516135251p:plain
Copy Filesにmyo.frameworkを追加

セットアップ完了です。
これでmyo.frameworkを使えるようになり、Myoアームバンドで計測したデータを取得することができます。

【機械学習】識別器の学習に使用するデータの数による識別精度の比較

イントロ

機械学習では、識別器を作成するのに学習データを使用します。もちろん学習データは多いほうがいいですが、最低どのくらい必要か気になったので実験していきます。
本記事では、生体信号の筋電波形をMyoで計測したデータの中から、学習データに使用するデータ数を1、2、3…と順に増やしていき、各学習データ数における識別率を比較していきます。
使用したデータのリンク(Mendeley Data - EMG data of the Myo Armband

方法

学習データ数を1、2、3…と増やしていき、最大数を7として各学習データ数における識別率を計測します。
学習データをtrainデータとtestデータに分割します。
識別率の比較には、
・trainデータに対して交差検証を行った時の識別率
・trainデータで学習した識別器でtestデータを分類したときの識別率
・50データ中の学習データ以外の未知データに対して分類したときの識別率
をグラフにプロットして比較します。

実装

1. データのファイルパスの設定
import numpy as np
import random

# 各ジェスチャのファイルパス
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]
2. データを呼び出すための関数の定義

以下のpath_to_data関数ではジェスチャのファイルパスとデータ番号を引数として渡すことで、引数に該当する筋電波形のデータのリストを取得できる。

# 取得したいジェスチャのデータをファイルパスとデータ番号から筋電データを取得する
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


以下のdata_load関数では、50データから任意のデータ番号をリストで指定することで、該当するデータ番号のサンプルのリストとサンプルとマッチするクラスラベルのリストを取得できる。

# データの読み込み
# rand_num: データ番号リスト
def data_load(rand_num):
    gesture = 1 # 動作のクラス値の変数
    all_data = [] # 全ての動作データの保存配列
    
    for p in path:
        # print(p)
        # データの取得
        data = path_to_data(p, rand_num)

        # 全ての特徴データとクラスデータを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
    # classラベル
    # gesture_array
    return emg_data, gesture_array


以下のload_train_test()関数では、random_stateで乱数シード値を指定して、kで学習データに使用したいデータの数を指定することで、50データから学習データと未知データを生成する。

# データの読み込み
def load_train_test(random_state = 0, k = 2):
    random.seed(random_state)
    train_num = random.sample(range(50), k = k) # 50データ中から学習データに使用するデータ番号を選択

    # 学習データの読み込み
    emg_train, gesture_train = data_load(train_num)
    print(train_num)

    # 未知データの番号リスト
    test_num = list(range(50))
    for num in train_num:  
        test_num.remove(num)
    print(test_num)

    # テストデータの読み込み
    emg_test, gesture_test = data_load(test_num)
    
    return emg_train, emg_test, gesture_train, gesture_test
3. 識別器を生成するための関数の定義
from sklearn.svm import SVC # SVMライブラリ
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis # 線形判別分析ライブラリ
from sklearn.neighbors import KNeighborsClassifier # k近傍法ライブラリ
from sklearn.model_selection import train_test_split # データ分割ライブラリ
from sklearn.model_selection import cross_val_score # 交差検証ライブラリ

# 各識別モデル生成用の関数
def generate_dicriminater(x_trainval, y_trainval):
    
    x_train, x_test, y_train, y_test = train_test_split(x_trainval, y_trainval, test_size=0.3, random_state=0)
    
    # 識別器の保存リスト
    model_list = []
    
    # 識別器名リスト
    name_list = ["svm_linear", "svm_poly", "svm_rbf", "svm_sigmoid", "lda", "knn"]
    
    # score_list
    val_score_list = []
    test_score_list = []
    
    # SVM識別器の生成
    kernel_str = ["linear", "poly", "rbf", "sigmoid"]
    param_list = [0.001, 0.01, 0.1, 1, 10, 100]
    for k in kernel_str:
        print(k)
        best_score = 0
        best_parameters = {}
        # 交差検証を用いたグリッドサーチ
        for gamma in param_list:
            for C in param_list:
                # SVCのインスタンス生成
                model = SVC(kernel = k, gamma=gamma, C=C, random_state=None)
                # 交差検証 パラメータcvで分割方法を設定
                scores = cross_val_score(model, x_train, y_train, cv=5)
                # 交差検証による評価値の平均
                score = np.mean(scores)
                if score>best_score:
                    best_score = score
                    best_parameters = {'gamma' : gamma, 'C' : C}

        # もっともスコアの高いパラメータで識別器をインスタンス
        model = SVC(kernel=k, **best_parameters)
        
        # モデルの保存
        model_list.append(model)
    
        # best_parametersにおける識別器を作成
        model.fit(x_train, y_train)
        
        # testデータの評価
        test_score = model.score(x_test, y_test)
        
        # スコアの保存
        val_score_list.append(best_score)
        test_score_list.append(test_score)

        print('Best score on validation set: {}'.format(best_score))
        print('Best parameters: {}'.format(best_parameters))
        print('Test set score with best parameters: {}\n'.format(test_score))
        

    # 線形判別分析の識別器生成
    lda = LinearDiscriminantAnalysis()
    model_list.append(lda)

    # 交差検証
    scores = cross_val_score(lda, x_train, y_train, cv=5)
    val_score = np.mean(scores)

    # testデータの評価
    lda.fit(x_train, y_train)
    test_score =  lda.score(x_test, y_test)
    
    # スコアの保存
    val_score_list.append(val_score)
    test_score_list.append(test_score)

    print("LDA")
    print("Score on validation set: {}".format(val_score))
    print("test_set score: {}\n".format(test_score))


    # k近傍法の識別器生成
    knn = KNeighborsClassifier(n_neighbors = 3)
    model_list.append(knn)

    # 交差検証
    scores = cross_val_score(knn, x_train, y_train, cv=5)
    val_score = np.mean(scores)

    # testデータの評価
    knn.fit(x_train, y_train)
    test_score =  knn.score(x_test, y_test)
    
    # スコアの保存
    val_score_list.append(val_score)
    test_score_list.append(test_score)

    print("knn")
    print("Score on validation set: {}".format(val_score))
    print("test_set score: {}\n".format(test_score))
    
    return model_list, val_score_list, test_score_list, name_list
4. 識別率のリストを生成する
# データ数による識別率の変動の比較
index_list = np.arange(1,9)
del_index = np.where(index_list == 5)
index_list = np.delete(index_list, del_index)
for k in index_list:
    print("Number {}\n".format(k))
    # emgとgestureの学習データと未知データの呼び出し
    emg_train, emg_unknown, gesture_train, gesture_unknown = load_train_test(random_state=1, k=k)

    # 識別器の生成と生成時の識別率の呼び出し
    model_list, val_score_list, test_score_list, name_list = generate_dicriminater(emg_train, gesture_train)
    
    un_score_list = []
    for m in model_list:
        m.fit(emg_train, gesture_train)
        un_score = m.score(emg_unknown, gesture_unknown)
        un_score_list.append(un_score)

    # list型からnp.array型に変換
    val_score_arr = np.array(val_score_list)
    test_score_arr = np.array(test_score_list)
    un_score_arr = np.array(un_score_list)
    
    if k == index_list[0]:
        val_score_summary = val_score_arr
        test_score_summary = test_score_arr
        un_score_summary = un_score_arr
    elif k > index_list[0]:
        val_score_summary = np.vstack([val_score_summary, val_score_arr])
        test_score_summary = np.vstack([test_score_summary, test_score_arr])
        un_score_summary = np.vstack([un_score_summary, un_score_arr])
    
    print(val_score_summary)
    print(test_score_summary)
    print(un_score_summary)

print("Finish")
    
# 転置処理
val_summ = val_score_summary.T
test_summ = test_score_summary.T
un_summ = un_score_summary.T

print("valiation score")
print(val_summ)
print("\n")
print("test score")
print(test_summ)
print("\n")
print("unknown score")
print(un_summ)
5. 識別率をグラフにプロット
# 学習データの数による識別率の違いをグラフ化

import matplotlib.pyplot as plt

data_num = np.arange(1,len(val_summ[0])+1)

fig = plt.figure(figsize = (13,15))

ax1 = fig.add_subplot(311)
for i in range(len(val_summ)):
    # ドットの配置
    ax1.scatter(data_num, val_summ[i], label=name_list[i]) 
    # ラインの配置
    ax1.plot(data_num, val_summ[i])
# グラフタイトル
plt.title("cross validation score")
# 軸ラベルの表示
plt.xlabel("Number of trials")
plt.ylabel("Identification rate")
# 横軸のtickを変更
plt.xticks(data_num)
plt.yticks(np.arange(0, 1.1, 0.1))
# グリッド線
plt.grid()
# 凡例の表示とグラフの表示
plt.legend(loc='lower right')

ax2 = fig.add_subplot(312)
for i in range(len(val_summ)):
    # ドットの配置
    ax2.scatter(data_num, test_summ[i], label=name_list[i]) 
    # ラインの配置
    ax2.plot(data_num, test_summ[i])

plt.title("test score")
# 軸ラベルの表示
plt.xlabel("Number of trials")
plt.ylabel("Identification rate")
# 横軸のtickを変更
plt.xticks(data_num)
plt.yticks(np.arange(0, 1.1, 0.1))
# グリッド線
plt.grid()
# 凡例の表示とグラフの表示
plt.legend(loc='lower right')

ax3 = fig.add_subplot(313)
for i in range(len(val_summ)):
    # ドットの配置
    ax3.scatter(data_num, un_summ[i], label=name_list[i]) 
    # ラインの配置
    ax3.plot(data_num, un_summ[i])

plt.title("unknown score")
# 軸ラベルの表示
plt.xlabel("Number of trials")
plt.ylabel("Identification rate")
# 横軸のtickを変更
plt.xticks(data_num)
plt.yticks(np.arange(0, 1.1, 0.1))
# グリッド線
plt.grid()
# 凡例の表示とグラフの表示
plt.legend(loc='lower right')
実行結果

cross validation score:学習データを分割した内のtrainデータで交差検証をした時の識別率
test score:学習データを分割した内のtestデータを識別した時の識別率
unknown score:50データの内、学習データ以外の未知データを識別した時の識別率

f:id:Yunos:20200510084637p:plain

学習データ数が識別率に影響を与えているのは、unknown scoreの未知データに対するグラフでsvm_sigmoid以外は学習データ数が増加するにしたがって、識別率が上昇している。
svm_linear、svm_poly、lda、knnに関してはデータ数が2~3あたりから識別率がおおよそ0.95で変化していないため、学習データ数は2、3でいいと考えられる。

【機械学習】Pythonで学習データの組み合わせによる識別率の比較

イントロ

Myoで計測した8動作の積分筋電位(IEMG波形)の50データのうち、2データをランダムで抽出して、識別器の学習データに使用する。5回ランダムで学習データを抽出して異なる5つの学習データの組み合わせがあるときに、学習データの組み合わせによって識別率がどの程度変化するかをグラフにプロットして調べる。

方法

識別率の指標として、
・学習データをさらにtrainデータとtestデータに7:3に分割して、trainデータを5回交差検証した時の識別率の平均
・trainデータで学習した識別器でtestデータを分類した時の識別率
・学習データに使用した以外の残りの48データを識別した時の識別率
をグラフにプロットして比較する。

識別器にはSVM(4カーネル)、LDA、k-NNを使用する。SVMのパラメータはgammaとCのみをグリッドサーチによって決定してそのベストスコアを交差検証の識別率として使用する。

予想

予想として、各識別器において学習データの組み合わせが変化した時の識別率の変化は、学習データの精度によって識別率がかなり変わるため、±10%程度であると考える。

実装

1. データのファイルパスの設定
import numpy as np
import random

# 各ジェスチャのファイルパス
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]
2. データを呼び出すための関数の定義

以下のpath_to_data関数ではジェスチャのファイルパスとデータ番号を引数として渡すことで、引数に該当する筋電波形のデータのリストを取得できる。

# 取得したいジェスチャのデータをファイルパスとデータ番号から筋電データを取得する
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


以下のdata_load関数では、50データから任意のデータ番号をリストで指定することで、該当するデータ番号のサンプルのリストとサンプルとマッチするクラスラベルのリストを取得できる。

# データの読み込み
# rand_num: データ番号リスト
def data_load(rand_num):
    gesture = 1 # 動作のクラス値の変数
    all_data = [] # 全ての動作データの保存配列
    
    for p in path:
        # print(p)
        # データの取得
        data = path_to_data(p, rand_num)

        # 全ての特徴データとクラスデータを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
    # classラベル
    # gesture_array
    return emg_data, gesture_array


以下のload_train_test()関数では、random_stateで乱数シード値を指定して、kで学習データに使用したいデータの数を指定することで、50データから学習データと未知データを生成する。

# データの読み込み
def load_train_test(random_state = 0, k = 2):
    random.seed(random_state)
    train_num = random.sample(range(50), k = k) # 50データ中から学習データに使用するデータ番号を選択

    # 学習データの読み込み
    emg_train, gesture_train = data_load(train_num)
    print(train_num)

    # 未知データの番号リスト
    test_num = list(range(50))
    for num in train_num:  
        test_num.remove(num)
    print(test_num)

    # テストデータの読み込み
    emg_test, gesture_test = data_load(test_num)
    
    return emg_train, emg_test, gesture_train, gesture_test
3. 識別器を生成するための関数の定義
from sklearn.svm import SVC # SVMライブラリ
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis # 線形判別分析ライブラリ
from sklearn.neighbors import KNeighborsClassifier # k近傍法ライブラリ
from sklearn.model_selection import train_test_split # データ分割ライブラリ
from sklearn.model_selection import cross_val_score # 交差検証ライブラリ

# 各識別モデル生成用の関数
def generate_dicriminater(x_trainval, y_trainval):
    
    x_train, x_test, y_train, y_test = train_test_split(x_trainval, y_trainval, test_size=0.3, random_state=0)
    
    # 識別器の保存リスト
    model_list = []
    
    # 識別器名リスト
    name_list = ["svm_linear", "svm_poly", "svm_rbf", "svm_sigmoid", "lda", "knn"]
    
    # score_list
    val_score_list = []
    test_score_list = []
    
    # SVM識別器の生成
    kernel_str = ["linear", "poly", "rbf", "sigmoid"]
    param_list = [0.001, 0.01, 0.1, 1, 10, 100]
    for k in kernel_str:
        print(k)
        best_score = 0
        best_parameters = {}
        # 交差検証を用いたグリッドサーチ
        for gamma in param_list:
            for C in param_list:
                # SVCのインスタンス生成
                model = SVC(kernel = k, gamma=gamma, C=C, random_state=None)
                # 交差検証 パラメータcvで分割方法を設定
                scores = cross_val_score(model, x_train, y_train, cv=5)
                # 交差検証による評価値の平均
                score = np.mean(scores)
                if score>best_score:
                    best_score = score
                    best_parameters = {'gamma' : gamma, 'C' : C}

        # もっともスコアの高いパラメータで識別器をインスタンス
        model = SVC(kernel=k, **best_parameters)
        
        # モデルの保存
        model_list.append(model)
    
        # best_parametersにおける識別器を作成
        model.fit(x_train, y_train)
        
        # testデータの評価
        test_score = model.score(x_test, y_test)
        
        # スコアの保存
        val_score_list.append(best_score)
        test_score_list.append(test_score)

        print('Best score on validation set: {}'.format(best_score))
        print('Best parameters: {}'.format(best_parameters))
        print('Test set score with best parameters: {}\n'.format(test_score))
        

    # 線形判別分析の識別器生成
    lda = LinearDiscriminantAnalysis()
    model_list.append(lda)

    # 交差検証
    scores = cross_val_score(lda, x_train, y_train, cv=5)
    val_score = np.mean(scores)

    # testデータの評価
    lda.fit(x_train, y_train)
    test_score =  lda.score(x_test, y_test)
    
    # スコアの保存
    val_score_list.append(val_score)
    test_score_list.append(test_score)

    print("LDA")
    print("Score on validation set: {}".format(val_score))
    print("test_set score: {}\n".format(test_score))


    # k近傍法の識別器生成
    knn = KNeighborsClassifier(n_neighbors = 3)
    model_list.append(knn)

    # 交差検証
    scores = cross_val_score(knn, x_train, y_train, cv=5)
    val_score = np.mean(scores)

    # testデータの評価
    knn.fit(x_train, y_train)
    test_score =  knn.score(x_test, y_test)
    
    # スコアの保存
    val_score_list.append(val_score)
    test_score_list.append(test_score)

    print("knn")
    print("Score on validation set: {}".format(val_score))
    print("test_set score: {}\n".format(test_score))
    
    return model_list, val_score_list, test_score_list, name_list
4. 識別率のリストを生成する
# データ番号による識別率の変動の取得
for i in [0,1,2,5,6]:
    print("Number {}\n".format(i))
    # emgとgestureの学習データと未知データの呼び出し
    emg_train, emg_unknown, gesture_train, gesture_unknown = load_train_test(random_state=i, k=2)

    # 識別器の生成と生成時の識別率の呼び出し
    model_list, val_score_list, test_score_list, name_list = generate_dicriminater(emg_train, gesture_train)
    
    un_score_list = []
    for m in model_list:
        m.fit(emg_train, gesture_train)
        un_score = m.score(emg_unknown, gesture_unknown)
        un_score_list.append(un_score)

    # list型からnp.array型に変換
    val_score_arr = np.array(val_score_list)
    test_score_arr = np.array(test_score_list)
    un_score_arr = np.array(un_score_list)
    
    if i == 0:
        val_score_summary = val_score_arr
        test_score_summary = test_score_arr
        un_score_summary = un_score_arr
    elif i > 0:
        val_score_summary = np.vstack([val_score_summary, val_score_arr])
        test_score_summary = np.vstack([test_score_summary, test_score_arr])
        un_score_summary = np.vstack([un_score_summary, un_score_arr])
    
    print(val_score_summary)
    print(test_score_summary)
    print(un_score_summary)
5. スコアのリストを転置する
val_summ = val_score_summary.T
test_summ = test_score_summary.T
un_summ = un_score_summary.T

print("valiation score")
print(val_summ)
print("\n")
print("test score")
print(test_summ)
print("\n")
print("unknown score")
print(un_summ)
6. 識別率をグラフにプロット
import matplotlib.pyplot as plt

data_num = np.arange(1,len(val_summ[0])+1)


fig = plt.figure(figsize = (13,15))

ax1 = fig.add_subplot(311)
for i in range(len(val_summ)):
    # ドットの配置
    ax1.scatter(data_num, val_summ[i], label=name_list[i]) 
    # ラインの配置
    ax1.plot(data_num, val_summ[i])
# グラフタイトル
plt.title("cross validation score")
# 軸ラベルの表示
plt.xlabel("Number of trials")
plt.ylabel("Identification rate")
# 横軸のtickを変更
plt.xticks(data_num)
plt.yticks(np.arange(0, 1.1, 0.1))
# グリッド線
plt.grid()
# 凡例の表示とグラフの表示
plt.legend(loc='lower right')

ax2 = fig.add_subplot(312)
for i in range(len(val_summ)):
    # ドットの配置
    ax2.scatter(data_num, test_summ[i], label=name_list[i]) 
    # ラインの配置
    ax2.plot(data_num, test_summ[i])

plt.title("test score")
# 軸ラベルの表示
plt.xlabel("Number of trials")
plt.ylabel("Identification rate")
# 横軸のtickを変更
plt.xticks(data_num)
plt.yticks(np.arange(0, 1.1, 0.1))
# グリッド線
plt.grid()
# 凡例の表示とグラフの表示
plt.legend(loc='lower right')

ax3 = fig.add_subplot(313)
for i in range(len(val_summ)):
    # ドットの配置
    ax3.scatter(data_num, un_summ[i], label=name_list[i]) 
    # ラインの配置
    ax3.plot(data_num, un_summ[i])

plt.title("unknown score")
# 軸ラベルの表示
plt.xlabel("Number of trials")
plt.ylabel("Identification rate")
# 横軸のtickを変更
plt.xticks(data_num)
plt.yticks(np.arange(0, 1.1, 0.1))
# グリッド線
plt.grid()
# 凡例の表示とグラフの表示
plt.legend(loc='lower right')
実行結果

cross validation score:学習データを分割した内のtrainデータで交差検証をした時の識別率
test score:学習データを分割した内のtestデータを識別した時の識別率
unknown score:50データの内、学習データ以外の未知データを識別した時の識別率

f:id:Yunos:20200508190953p:plain

グラフの変動から学習データの組み合わせの違いによる識別率の変化は±10%程度である。
また、サポートベクターマシンカーネルがrbfとsigmoidの場合、識別率が低いことからデータがIEMGであるときには、識別器の使用にはサポートベクタマシンのrbfもしくはsigmoidの使用は向かないと考えられる。

skearnを使用したk-NN(K Nearest Neighbor:k近傍法)の実装方法

はじめに

本記事では、sklearnのk-NN(K Nearest Neighbor)のライブラリを使用してアヤメのクラス分類をしながら、k-NNの実装方法を記述していきます。

k-NNとは?

k-NNは、入力された未知データに対して、もっとも似ている学習データをK個選択して、選択された学習データの中で最も多く選ばれているクラスを未知データのクラスとして予測する手法です。

実装方法

1. データの読み込み

# 前処理 データセットのダウンロード

%matplotlib inline

from sklearn import datasets
import numpy as np
from sklearn.model_selection import train_test_split

# アヤメデータセットを用いる
iris = datasets.load_iris()
# データセットのdict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

# 例として、3,4番目の特徴量の2次元データで使用
# iristデータの特徴量['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']の3、4番目を使用
#x = iris.data[:, [2,3]]
x = iris.data[:, [0,1]]
#x = iris.data

# クラスラベルを取得
# target_names : ['setosa' 'versicolor' 'virginica']
y = iris.target

# データの分割
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=0)

2. 交差検証による学習データの識別率評価

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score

knn = KNeighborsClassifier(n_neighbors = 6)

knn.fit(x_train, y_train)

scores = cross_val_score(knn, x_train, y_train, cv = 5)
print(scores)

print("k-NNによる交差検証の識別率平均:{}".format(np.mean(scores)))

実行結果

[0.66666667 0.76190476 0.76190476 0.80952381 0.80952381]
k-NNによる交差検証の識別率平均:0.7619047619047619

3. 識別状況の可視化

# データのプロット

import matplotlib.pyplot as plt
from mlxtend.plotting import plot_decision_regions
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=0)

# データの可視化
plt.style.use('ggplot')

# trainデータとtestデータを連結
x_combined_std = np.vstack((x_train, x_test))
y_combined = np.hstack((y_train, y_test))

fig = plt.figure(figsize=(13, 8))
plot_decision_regions(x_combined_std, y_combined, clf=knn, res=0.02)
plt.show()

実行結果

f:id:Yunos:20200507092700p:plain
k-NNのクラス分類の可視化

sklearnを使用したLDA(Linear Discriminant Analysis:線形判別分析)の実装方法

はじめに

本記事では、sklearnのLDA(Linear Discriminant Analysis)のライブラリを使用してアヤメのクラス分離をしながら、LDAの実装方法を記述していく。

LDAとは?

複数の次元をもつデータを、データが持つ情報を保ちながら次元を減らし、データを分離する次元削除手法です。

例えば、2次元座標上に(a, b)、(c, d), (e, f)... と2次元の成分をもつデータの集まりがあるとします。まず、この集合内のデータを新たな軸に写像して、(a, b) → (x)、(c, d) → (y)、(e, f) → (z)... と新たな軸上で1次元の座標に変換します。このときに、1次元の座標が最もよく分離できる軸を見つけて、境界線を引くことでデータの判別を行います。
この新しい軸を見つけるための指標は、LDAでは座標が属するクラスとクラスの間の分散は最大にし、クラス内での分散は最小にすることで新しい軸を見つけます。

実装方法

1. データの読み込み

# 前処理 データセットのダウンロード

%matplotlib inline

from sklearn import datasets
import numpy as np

# アヤメデータセットを用いる
iris = datasets.load_iris()
# データセットのdict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

# 例として、3,4番目の特徴量の2次元データで使用
# iristデータの特徴量['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']の3、4番目を使用
#x = iris.data[:, [2,3]]
x = iris.data[:, [0,1]]
#x = iris.data

# クラスラベルを取得
# target_names : ['setosa' 'versicolor' 'virginica']
y = iris.target


2. LDAの識別器の学習と交差検証による識別率評価

# 識別器の設定

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import cross_val_score

lda = LinearDiscriminantAnalysis()
lda.fit(x_train, y_train)

score = lda.score(x_test, y_test)

scores = cross_val_score(lda, x_test, y_test, cv=5)


print('LDAの識別率:{}'.format(score))

print('交差検証による識別率の平均:{}'.format(np.mean(scores)))

実行結果

LDAの識別率:0.8
交差検証識別率:0.7333333333333333


識別状況の可視化

# データのプロット

import matplotlib.pyplot as plt
from mlxtend.plotting import plot_decision_regions

# データの可視化
plt.style.use('ggplot')

# trainデータとtestデータを連結
x_combined_std = np.vstack((x_train, x_test))
y_combined = np.hstack((y_train, y_test))

fig = plt.figure(figsize=(13, 8))
plot_decision_regions(x_combined_std, y_combined, clf=lda, res=0.02)
plt.show()

実行結果

f:id:Yunos:20200506113617p:plain
LDAによる識別状況


識別率が低い理由として、データが混じりあっているところがあるためであると考えられる。

最後に

sklearnライブラリを使用することで、かなり簡単に識別器を実装することができました。
ライブラリは偉大だなと思いました。

SVMの各カーネルにおけるハイパラメータと識別精度を比較する

はじめに

 本記事では、SVMを使用する際にもっとも適したカーネルを選択する方法として、グリッドサーチと交差検証によって、カーネルのバンド幅gammaと正則化パラメータCを決定し、各カーネルのもっとも良いパラメータを比較する。
 データセットはアヤメのデータセットを使用する。

データセットのロード

# 前処理 データセットのダウンロード

%matplotlib inline

from sklearn import datasets
import numpy as np

# アヤメデータセットを用いる
iris = datasets.load_iris()
# データセットのdict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

# 例として、3,4番目の特徴量の2次元データで使用
# iristデータの特徴量['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']の3、4番目を使用
x = iris.data[:, [2,3]]

# クラスラベルを取得
# target_names : ['setosa' 'versicolor' 'virginica']
y = iris.target

交差検証・グリッドサーチの実装

gammaとCのパラメータは0.001, 0.01, 0.1, 1, 10, 100について探索して、適したパラメータを決定します。

# モデルの学習~精度評価
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
from mlxtend.plotting import plot_decision_regions

x_trainval, x_test, y_trainval, y_test = train_test_split(iris.data, iris.target, random_state=0)
x_train, x_valid, y_train, y_valid = train_test_split(x_trainval, y_trainval, random_state=0)

kernel_str = ["linear", "poly", "rbf", "sigmoid"]
param_list = [0.001, 0.01, 0.1, 1, 10, 100]

for k in kernel_str:
    print(k)
    best_score = 0
    best_parameters = {}
    # 交差検証を用いたグリッドサーチ
    for gamma in param_list:
        for C in param_list:
            # SVCのインスタンス生成
            model = SVC(kernel = k, gamma=gamma, C=C, random_state=None)
            # 交差検証 パラメータcvで分割方法を設定
            scores = cross_val_score(model, x_trainval, y_trainval, cv=5)
            # 交差検証による評価値の平均
            score = np.mean(scores)
            if score>best_score:
                best_score = score
                best_parameters = {'gamma' : gamma, 'C' : C}
    
    # もっともスコアの高いパラメータで識別器をインスタンス
    model = SVC(kernel=k, **best_parameters)
    # best_parametersにおける識別器を作成
    model.fit(x_trainval, y_trainval)
    # testデータの評価
    test_score = model.score(x_test, y_test)
    
    print('Best score on validation set: {}'.format(best_score))
    print('Best parameters: {}'.format(best_parameters))
    print('Test set score with best parameters: {}'.format(test_score))

実行結果は以下のようになる。

linear
Best score on validation set: 0.9731225296442687
Best parameters: {'gamma': 0.001, 'C': 1}
Test set score with best parameters: 0.9736842105263158
poly
Best score on validation set: 0.9731225296442687
Best parameters: {'gamma': 0.01, 'C': 100}
Test set score with best parameters: 0.9736842105263158
rbf
Best score on validation set: 0.9731225296442687
Best parameters: {'gamma': 0.1, 'C': 10}
Test set score with best parameters: 0.9736842105263158
sigmoid
Best score on validation set: 0.9553359683794467
Best parameters: {'gamma': 0.001, 'C': 100}
Test set score with best parameters: 0.9736842105263158