質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1797閲覧

LSTMモデルでテニスの打球方向の予測を行う際のデータセットの構成について

map_app2

総合スコア2

Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/12/31 13:13

テニスにおける打球方向の予測を行いたいと思ってるのですがデータセットを作る際正解データをどこに記せば良いのかがわかりません。
打球時から20フレーム前までの選手の位置座標と選手の関節位置座標から打球方向のクラスを出力するLSTMモデルを組もうと考えています。個人的には打球方向をどこに記せば良いのかがよくわかっていません。
自分でも各業にそれぞれ正解データを降るのはおかしいと思っていますがどのようにすれば良いのかがわかりません。
現状自分が作っっているデータセットをイメージとして載せておきます。
はじめの二列が選手のコート内の座標でそこから26列が選手の関節の座標で最後の一列が打球のコースになります。
各行に1フレーム分の座標情報になります。
20行ごとに1回打球のコースをstraight,leftcross,rightcrossの3クラスとして出力したいと思っています。
機械学習初心者のため拙い説明担ってしまい申し訳ありません。
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

PondVillege

2022/12/31 19:18

そのデータセットは,どこからかダウンロード可能なものでしょうか,不可能であれば疑似データでコードを例示します.
map_app2

2022/12/31 20:17

このデータセットは自分で作成したものなのでダウンロードは出来ないです。申し訳ありません。擬似データでも良いのでコードを例示していただけるとありがたいです。
PondVillege

2023/01/01 00:17 編集

疑似データを9-16行目に生成してコードを例示しました. raw_data = pd.read_csv("dataset.csv").values に置き換えて利用ください.
guest

回答1

0

ベストアンサー

各行にそれぞれ正解データを振るのはおかしいと思っています

普通は,画像/時系列データ等の非構造化データの場合,1フォルダに1クラス,1ファイルに1データが基本です.したがって,次のようなディレクトリ構造にするのが多いです.波形データであればkeras.utils.audio_dataset_from_directoryが使えるぐらい一般的な構造です.

dataset ├── straight │ ├── 01.csv │ ├── 02.csv │ └── ... ├── leftcross │ ├── 01.csv │ ├── 02.csv │ └── ... └── rightcross ├── 01.csv ├── 02.csv └── ...

例えば,SIGNATEの練習問題株価の推移予測では,最後の行にのみ,出力したいデータが挿入されています.同様に,質問にあるように各行に出力したいデータを挿入したとしても,コードを記述して機械学習で扱えるデータに変換すればよいだけのことです.ちなみに,このような一般的でないデータの場合,学習可能な形式に変換するライブラリは存在しません.ひたすら自分でコードを書くしかないです.

データセットを作る際出力データをどこに記せば良いのか

出力データを記す位置は,入力データとの対応関係がとれるのであればどこでも良いです.どんな構造であれ,最終的にモデルに与える入力データ配列xi番目にアクセスしたときの内容は,出力データ配列yi番目の内容を意味するよう,プログラムを組むことになります.この対応関係が取り出せれば,上のようなディレクトリ構造であっても,質問のようなCSVファイルでも大丈夫です.自分がコードを書きやすいようにCSVファイルを作ったらいいんじゃないでしょうか.

今回の目的に応じたデータ加工の最終目標は,入力データの形状が(100, 20, 28),出力データの形状が(100, 3)になることです.特に入力のデータ形状はLSTMが扱える形式になっています.データのi番目だけの単一のデータをモデルに与えた場合,次のように入出力されることが期待されます.

この状態を目指せるデータセットを構築するまでのことです.

以下余談

以下にコードを例示します.画像で示しているdataset.csvをpandas.read_csv("dataset.csv")で読み込むことが可能です.ファイルパスは適宜環境に合わせてください.

前回の質問で得られた解答にあるような,chunksize = 20の指定と同等の動作を,スライスを利用して実現しています.念のため,各行に割り振ったstraight, leftcross, rightcrossが20行(1データ分)の間で一致しなかった場合assertion errorを吐くようにしてあります.

Python

1from sklearn.preprocessing import OneHotEncoder, StandardScaler 2from sklearn.model_selection import train_test_split 3 4import numpy as np 5import pandas as pd 6import random 7 8# raw_data = pd.read_csv("dataset.csv").values と同等の疑似データを作成する. 9raw_data = list() 10for i in range(100): # 100打球分 11 shoot_type = random.sample(["straight", "leftcross", "rightcross"], 1)[0] 12 for k in range(20): # pre shoot behavior 13 d = [random.gauss(0, 100) for _ in range(28)] 14 d.append(shoot_type) 15 raw_data.append(d) 16raw_data = np.array(raw_data) 17# 作成完了, raw_data.shape = (2000, 29) のはず. 18 19# 前処理フェーズ 20# 入力データをFeature-wiseでZ-Score Normalizationする. 21ss = StandardScaler() 22raw_data[:, :-1] = ss.fit_transform(raw_data[:, :-1].astype(np.float32)) 23 24x, y = list(), list() # 入力データxと出力データyに整形する. 25for i in range(0, len(raw_data), 20): 26 x.append(raw_data[i: i + 20, :-1]) # 入力データを取得 27 assert len(np.unique(raw_data[i: i + 20, -1])) == 1, f"wrong data format at {i} to {i+20}, {raw_data[i: i + 20]}" 28 y.append(raw_data[i, -1]) # 出力データを取得 29x = np.array(x).astype(np.float32) 30print(x.shape) # (100, 20, 28) になっているはず.100件,タイムスタンプ20点,特徴量28個を意味する 31 32# 出力データをOne-Hot Encodingする. 33ohe = OneHotEncoder() 34y = ohe.fit_transform(y.reshape(-1, 1)).toarray() 35print(y.shape) # (100, 3) になっているはず.100件,3クラスを意味する 36 37x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.1) # 1割をテストデータにする. 38# 前処理フェーズ終了

タグがKerasなので,このままKerasで書き進めると,次のように学習が可能です.

Python

1from keras.layers import Input, Dense, LSTM 2from keras.models import Sequential 3from keras.optimizers import Adam 4from keras.callbacks import EarlyStopping, ReduceLROnPlateau 5from keras.utils import plot_model 6from sklearn.metrics import accuracy_score, confusion_matrix 7 8# 単純なLSTMモデルを作成する. 9model = Sequential() 10model.add(Input(shape = (20, 28))) # Timestampが20件, Featureが28件のデータから予測したい 11model.add(LSTM(32)) 12model.add(Dense(3, activation = "softmax")) # 3クラスを予測する. 13# モデル作成完了. 14 15# モデルの状態を確認する. 16model.summary() 17plot_model( # required graphviz 18 model, 19 to_file = "model.png", 20 show_shapes = True, 21 show_layer_names = True, 22 show_layer_activations = True 23) 24# モデルの損失関数と最適化関数,学習過程で監視したいMetricsを設定 25model.compile( 26 loss = "categorical_crossentropy", 27 optimizer = Adam(learning_rate = 0.001), 28 metrics = ["acc"] 29) 30 31# 学習が停滞(学習曲線がPlateau状態)していたら,学習率LRを減少Reduceさせる. 32rlr = ReduceLROnPlateau( 33 patience = 5, # 5epoch間で 34 monitor = "loss", # lossの 35 mode = "min", # 最低値minが更新されなかったら 36 factor = 0.5, # 学習率を0.5倍にする 37 verbose = 0, 38) 39 40# 学習 41model.fit( 42 x_train, y_train, 43 batch_size = 32, 44 epochs = 256, 45 validation_split = 0.1, # trainデータから更に1割を検証用データにする 46 callbacks = [rlr] 47) 48 49# テスト 50y_test = np.argmax(y_test, axis = -1) 51y_pred = np.argmax(model.predict(x_test), axis = -1) 52print(f"Confusion Matrix\n{confusion_matrix(y_test, y_pred)}") 53print(f"Accuracy Score\n{accuracy_score(y_test, y_pred)}")

今回かなり適当に書き進めましたが,学習において全て正しい手法/値を利用しているというわけではないことに注意してください.全ての入力においてStandardScalerによるFeature-wiseな標準化をしていることや,テストデータおよび検証データの利用にあたっては乱数で適当に分割しているだけですし,LSTM内のユニット数を適当に32で決めていたり,オプティマイザにAdamの利用をしたり学習率を0.001にしたり,バッチサイズを32にしたりなどしているため,調整の余地しかないです.

少なくとも,データの整形/前処理に関して学習されることから始められることを推奨します.次に列挙する資料の確認をしてください.

  • sklearn.preprocessing: 正規化に関して
    • 前処理22-23行目: 正規化方法はZ-Scoreのみならず,他にもMin-Max Normalizatinなどがあること.
      • 今回はFeature-wiseに正規化したが,他にもDataset-wise, Sample-wiseな正規化があること.
    • 前処理34-35行目: OneHot Encodingの動作について.
  • sklearn.model_selection: 交叉検証に関して
    • 今回は簡単にtrain_test_splitでデータの分割を済ませたが,本来であれば交叉検証によるモデルの最適化が必要であること.

今回コードに含まれていませんが,CSVファイルを見る限り座標が0になっている箇所は欠損値かと存じます,これらを補間する手法も多岐にわたるので調べて実装なさった方が良いでしょう.

Google Search - 欠損値 補間

また,SIGNATEにはテニスの勝敗予測をする練習問題があるので,初手で時系列データに取り組んでわけわからないまま進めるよりは,このような単純なテーブルデータから取り組んでみても良いでしょう.少なくとも勝敗予測できなければ,時系列データなんて予測できないと思います.

まとめ

  • 出力データを記す位置は,入力データとの対応関係がとれるのであればどこでも良い
    • フォルダごとにクラスで分ける,行末に記す,別ファイルに記す,データベースを利用する等
  • Neural Networkの利用を前提としているなら,データの前処理の知識が必要不可欠であること.Garbage In, Garbage Outである.
  • 初手で独自データの解析をすると痛い目を見る(見た)

投稿2022/12/31 23:48

編集2023/01/01 00:56
PondVillege

総合スコア1579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

map_app2

2023/01/01 02:53

ありがとうございます。 一度試して気になるとこがあれば再度質問させていただきます。
PondVillege

2023/01/02 02:08

構いませんが,最低限の事前調査は済ませておくようお願い致します.
map_app2

2023/01/02 02:18 編集

上のコードを実行したところ y = ohe.fit_transform(y.reshape(-1, 1)).toarray() AttributeError: 'list' object has no attribute 'reshape' というエラーが出ますがyもキャストする必要がありますでしょうか
PondVillege

2023/01/02 02:23

はい,ndarrayにしてください
map_app2

2023/01/02 07:56

度々すみません。 その後以下のようなエラーが出ましたが何が原因でしょうか。 File "model.py", line 48, in <module> model.add(Input(shape = (20, 28))) # Timestampが20件, Featureが28件のデータから予測したい File "/usr/local/lib/python3.6/dist-packages/keras/engine/sequential.py", line 133, in add 'Found: ' + str(layer)) TypeError: The added layer must be an instance of class Layer. Found: Tensor("input_1:0", shape=(None, 20, 28), dtype=float32) 自分で調べてみましたが解決できませんでした。
PondVillege

2023/01/02 10:58

Kerasのバージョンの低さによるものだと考えられます. もしかしたら今使っているバージョンでは model.add(Input(shape = (20, 28))) のところを model.add(InputLayer(input_shape = (20, 28))) にしてInputLayerをkeras.layersからimportしたら動くかもしれません. また,現在利用されているPythonも3.6とかなり前のものです. Kerasと共に最新バージョンの利用であれば,エラーは生じないものと思われます. https://github.com/keras-team/keras/issues/11848 によるとバージョンのアップデートを要求されています.
map_app2

2023/01/08 13:23

ありがとうございました。 自分のデータセットでも予測をすることができました。 最後に一つおききしたいのですが、 print(f"Confusion Matrix\n{confusion_matrix(y_test, y_pred)}") で混同行列を表示していますが、accuracyscoreの他にも混同行列を出力している理由は何でしょうか?
PondVillege

2023/01/08 16:26 編集

Accuracyを計算するのみならず,PrecisionやRecall等,他の評価値を求める基礎の表だからです. 分類問題を解くモデルを評価するにあたって,最初に見るのはConfusion Matrixであるべきです.そこから算出できるAccuracyやPrecision, Recall等の評価値を見てモデルの性能や実用性を検討/評価するのが一般的であると存じています. https://engineering.mobalab.net/2020/12/03/%E5%88%86%E9%A1%9E%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E6%8C%87%E6%A8%99precision%E3%81%A8recall%E3%81%AE%E8%A7%A3%E8%AA%AC/ また,Classificationに関する評価値は多様にあるので確認してください. https://scikit-learn.org/stable/modules/model_evaluation.html データセットにおける種類ごとのデータ数(今回はstraight, leftcross, rightcrossのデータ数)に偏りがある場合などは,accuracy_scoreではなくbalanced_accuracy_scoreなどを使う必要があります. また,Accuracyを出して「はい終わり」というわけではなく,先述のPrecisionやRecall等も算出し,モデルを評価し,汎化性が高くなるようハイパーパラメータチューニングの実施をする必要があります. 現状ただ「予測をした」だけで統計的/実用的に最適かどうか不明です.
map_app2

2023/01/19 00:13

あれからデータセットを増やし実験を続けているのですがy_predの値が0しか予測してくれませんがこれはデータセットの質が悪いのでしょうか。
map_app2

2023/01/19 00:39

またval_lloss,losがnan担ってしまいます、疑似データで動かしたときはこのようにはなりませんでした。
PondVillege

2023/01/19 03:30

いずれの問題もデータ本体やその前処理が悪いと考えます. 1. 欠損値はどのように処理していますか? 2. データの前処理は何を行なっていますか? 3. straight, rightcross, leftcrossの定義が曖昧で正しいデータセットでなかったりしますでしょうか? 4. そもそもLSTMに渡すデータの形式は正しいでしょうか.表示して確認してみましたか? 5. 学習時のlossの推移は,trainとvalidで比較してどのようになっていますか? 6. NaNになるのは,そのうちどのタイミングでしょうか?
map_app2

2023/01/20 18:10

1欠損値はフォアで打つ時とバックで打つ時に場合分けして各列0になってない値の平均を取って埋めています. 2前処理はMinMaxScalerで正規化を行っています. 3ラベルの定義とはどういうことでしょうか.提示していただいたコード通りワンホットエンコーディングを行っているつもりです. 4形式は20列1セットの28の特徴量で渡してあります. 5,6,確認方法が分かりませんが1エポック目からlosがNaNになっています
PondVillege

2023/01/21 22:03 編集

1. 心許ないですが影響ないものと考えて話を進めます. 2. MinMaxScalerを使うタイミングは,回答に例示したタイミングと同じでしょうか 3. 何か視覚的な根拠があって,どこに打球したかを定義したと存じます.クロスっぽいけどストレートとした.みたいなものはあるのでしょうか.感覚で決めたのか,それとも相手のコートの何分の何より外側であればクロス,みたいな数値的な定義で分類したのでしょうか. 4. ここで言う「表示して確認」は,そのタイムスタンプ20点の14関節をを動画的に描画して,視覚的にテニスの打球フォームであることが確認できるかどうか,という意味で聞きました.生データをある程度加工して,質問のCSVのデータセットを構築したものの,加工の過程にミスがあればフォームを再現するデータにならないと思いました. 5, 6. https://keras.io/ja/visualization/#_2 訓練の履歴の可視化が使えます.回答のサンプルコードが通って,質問のCSVデータで1エポック目からlossがNaNになるなら,教師データにNaNやinfinitがある可能性が高いです.データ全体を見渡して確認してもらうしかありません.オススメはやはり先述の「動画にして可視化する」ことですね.
map_app2

2023/01/23 11:49

2. raw_data[:, :-1] =ss.fit_transform(raw_data[:, :-1].astype(np.float32))のタイミングでやっています。 3.打球時の手前の選手のx座標とボールを返球した相手選手のx座標の差を求めて200以上、-200以下だったらクロスと決めてラベル付けをしています。 4.OpenPoseで各フレームの関節位置を確認しています。
PondVillege

2023/01/23 12:51

> 損失関数2categorical_crossentropyを使ってますがこの記事は関係あるのでしょうか? 純粋にTensorFlowを使う場合はそのようにしないとNaNになりますが, https://github.com/keras-team/keras/blob/v2.11.0/keras/backend.py#L5546 ライブラリを見て分かる通り,上記コードの記述であればepsilonとして微小値を挿入していますのでNaNになることはありません. 学習を行わずに,モデルに入力を与えて全てのデータが正しくモデルを通過できるか確認する必要がありそうです.具体的には,モデル作成直後, preds = model.predict(x_train) print(np.count_nonzero(np.isnan(preds))) や print(np.where(np.isnan(preds))) などの記述で,x_trainから予測値を得る際にNaNが出るかどうかを確認できます. ここでNaNが出るならデータセットの問題ですし,出ないなら逆伝搬時に勾配爆発した可能性があります.
map_app2

2023/01/23 13:10

1068というふうに出てきました。 おそらくデータセットの問題だと思います。
map_app2

2023/01/23 13:50

0である部分を他の数値で補間してもう一度実行してみます。
PondVillege

2023/01/23 17:42

softmaxを通したら,そのような値は出ないはずです.モデルがおかしい可能性も出てきましたね.
map_app2

2023/01/24 04:45

ですが例示されたコードのランダムでraw_dataを作ったときはlossがMaNにならないため、データセットがおかしいと思うのですが、マイナスの値があると正常に動かなかったりするのでしょうか?
PondVillege

2023/01/24 06:44

例示したコードでは平均0,標準偏差100のデータを生成したのち,StandardScalerで平均0,標準偏差1に正規化してます.ここでは負の値も入力値になってますが,ニューラルネットワークの扱える0前後の値を維持できているので問題ないです.同様にMinMaxScalerもデフォルト値のまま利用すれば0~1に正規化されるので問題ありません.(もし0~100とかに正規化したなら上記の問題は起きますけど)
map_app2

2023/01/24 06:45

自分のデータセットでも何度か実行するとloss、val_lossがNaNにならず正常に予測が行えるのですがlosがNaNになる時との違いが何なのか調べてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問