poko_amu
@poko_amu (poko amu)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

波形を異常検知させたい(断続的な波形データ)

ある設備の波形を異常検知させたい

初めに、かなりの初学者なので不備などあると思いますが、
解決に必要な情報が足りていない場合はお伝えいただけると幸いです。
よろしくお願いいたします。

早速中身です。
手法は異常検知なのですが
その中でもオートエンコーダを用いて実装しようと
実際に書いてみたのですが、うまくいっていない気がします


環境

使用OS : Windows10
使用言語 : Python


使用データについて

実際に使用しているデータの形は下記になります
コメント 2022-06-07 163618.png

波形にするとこんな形です
hakei.png

このデータは
1サイクル動作のデータになります

やりたいこと

時系列データにおける異常検知みたいなことがしたいです

その為、
モデルを作成したら、
1サイクル毎に上記のデータを取得し
作成したモデルに通してOKか異常かを判断したいです

この場合にこんなやり方が良い、や
このサイトを参考にすると良いを教えて頂けると嬉しいです

やったこと

参考になるかわかりませんが、一応私がやったことを書きます
①データを準備(取得できているデータがN=1の為、トレインもテストも同じデータを使用)
②「時系列 異常検知」と検索
③オートエンコーダを用いて異常検知実装
 (他にもやりましたが全て同じ疑問に当たりやっていることが間違っている気がしました)

まずは自分でネットを参考にしながら書いたものを載せておきます

# モジュールインポート
import os
from glob import glob

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.preprocessing import MinMaxScaler
import tensorflow as tf
from tensorflow.keras import layers,Model
from tensorflow.keras.layers import Input,Dense
from tensorflow.keras import regularizers

##### データの準備 #####
#フォルダ分けしてますが中身は同じです
# データ読み込み - train
Train_Csv_Path = "./csv/train/*.csv"
Train_Csv_Paths = glob(Train_Csv_Path)[0]

# データ読み込み - test 
Test_Csv_Path = "./csv/test/*.csv"
Test_Csv_Paths = glob(Test_Csv_Path)[0]
##### データの準備 #####


##### CSV読み込み ######
def Csv_Read(csv_path):
    df = pd.read_csv(csv_path, encoding="shift-jis", skiprows=[1])

    # データフレームを必要データに絞る
    an_df = pd.DataFrame(df["電動シリンダーストローク値"])

    return an_df

Train_df = Csv_Read(Train_Csv_Paths)
Test_df = Csv_Read(Test_Csv_Paths)
##### CSV読み込み ######


##### データを分割する #####
def subseq(x, w):
    seq = np.zeros((len(x) - w + 1, w))
    x_np = x.iloc[:, 0]
    for i in range(len(x) - w + 1):
        seq[i, :] = x_np[i:w+i]
    return pd.DataFrame(seq)

w_size = 100 # ウィンドウサイズ この考え方わからない→とりあえずテキトー
X_train = subseq(Train_df, w_size)
X_test = subseq(Test_df, w_size)
print(X_train.shape)
print(X_test.shape)
##### データを分割する #####



##### 正規化 #####
sc = MinMaxScaler()
# 正規化
X_train = sc.fit_transform(X_train)
X_test = sc.fit_transform(X_test)
##### 正規化 #####



##### モデル作成 #####
l1=10e-9  # L1正則化のパラメータ
enc_dim = 512  # 隠れ層のユニット数

cpu_input = Input(shape=w_size, name="cpu_input")
encoded_cpu = Dense(enc_dim, activation='relu', 
                activity_regularizer=tf.keras.regularizers.l1(l1))(cpu_input)
encoded_cpu = layers.Dropout(0.2)(encoded_cpu)

decoded_cpu = Dense(w_size, activation='relu',name="cpu_output1")(encoded_cpu)
# decoded_cpu = layers.Dropout(0.3)(decoded_cpu)
decoded_cpu = Dense(w_size, activation='relu',name="cpu_output2")(decoded_cpu)
 
autoencoder = Model(cpu_input, decoded_cpu)


autoencoder.compile(optimizer='adadelta',  
                    loss=['binary_crossentropy','binary_crossentropy'])
##### モデル作成 #####


##### 学習 #####
epochs = 500
history = autoencoder.fit(X_train,
                          X_train,
                          epochs=epochs,
                          batch_size=None,                    
                          verbose=1)
##### 学習 #####


##### 学習精度確認 #####
print("loss確認")
pd.DataFrame(history.history).plot()
##### 学習精度確認 #####


##### 推論 #####
decoded_cpu = pd.DataFrame(autoencoder.predict(X_test))
##### 推論 #####


##### 異常値確認 #####
dist_cpu = np.sqrt( np.sum( (decoded_cpu - X_train)**2, axis=1))
dist_cpu.plot(color="blue")
##### 異常値確認 ######

疑問点

疑問点①
結果が良ければよかったのですが
最後の異常値確認の部分のプロットを見ると

kekka.png

同じデータを渡しているのに
山が出来ている(異常度高い)為、何かがおかしいと感じました

疑問点②
学習に複数サイクル分のデータを入れる必要があるはずだが
単純に波形を繋げて学習させればいいのか?

→これ関しては実際にやってみましたが、
 推論させるデータが1サイクル分なので周期?が合わず
 異常度を出すための計算の時にエラーが出る

そもそもちゃんと推論できている?

最後に

現状はこんな状態です
まったく見当違いのことをしている可能性が出てきたので
質問させていただきました。
なんとかして、実装させたので、みなさんの力と知恵を貸していただけると嬉しいです
よろしくお願いいたします。

0

1Answer

  1. 1サイクルの動作データでの学習は、2つ点を与えてその点をつなぐ曲線を推測するようなもので意味をなしません。学習は正常なデータをいくつも用意して何が正常なのかをきちっと定義付けさせてあげないと異常検知も何もできませんよ。

  2. 複数サイクル分のデータの場合、まずサイクルごとに分割して、ピークの時間がが揃うように時間を前後させて、最低値がゼロになるように引き算して、最高値が1になるように割り算して、すべてのデータの長さが揃うように前後を0で埋めないといけないです。

昔から、機械学習で一番難しい(めんどうな)のは教師データの作成と言われていて、上記の2のような面倒な作業をしないといけなく、大抵そこに一番時間を割くことになります。

1Like

Comments

  1. @poko_amu

    Questioner

    回答頂きありがとうございます。
    返信が遅れてしまい大変申し訳ありません

    回答頂いた中で私の中で分からない部分がありますので質問させてください。
    ①データ量が不足しているのは重々承知しております。
     そのため、とりあえずは複製して学習させたいのですが、
     「サイクル毎に分割して」この部分が上手くできないです
    例えば、
    1サイクル100秒のデータが取れたとします
    それを5サイクル分学習させたいとなった時に、
    データフレームの形であらわすとなると、
    1列ごとに1サイクルのデータが格納されていれば良いということでしょうか?

    質問で記述したコード内の分割している部分は少し複雑で
    そんなに単純なことではないのかな?と思っています
    よろしければご教授ください



  2. > 1サイクル100秒のデータが取れたとします

    例えば、質問者さんが、シリンダーストロークについて何も知らない私みたいな人だったとしましょう。1つのデータをぽんと見せられて、それが異常か正常かをはんだんしろと言われて、判断ができるでしょうか?もしかしたら、これがたまたま異常なデータで、私達が知らないだけかもしれません。
    つまり、データ一つだで、正常を定義をするのは危険だという事です。

    5サイクル分学習させたい場合は、後4サイクル分後ろにつなげればいいと思います。

    ```
    _/^|_/^|_/^|_/^|_^|_
    ```
    みたいなグラフがかけるような入力を用意すればいいと思います。

    ```
    ##### データを分割する #####
    def subseq(x, w):
    seq = np.zeros((len(x) - w + 1, w))
    x_np = x.iloc[:, 0]
    for i in range(len(x) - w + 1):
    seq[i, :] = x_np[i:w+i]
    return pd.DataFrame(seq)

    w_size = 100 # ウィンドウサイズ この考え方わからない→とりあえずテキトー
    X_train = subseq(Train_df, w_size)
    X_test = subseq(Test_df, w_size)
    print(X_train.shape)
    print(X_test.shape)
    ##### データを分割する #####



    ##### 正規化 #####
    sc = MinMaxScaler()
    # 正規化
    X_train = sc.fit_transform(X_train)
    X_test = sc.fit_transform(X_test)
    ##### 正規化 #####
    ```

    良くソースを読むとここで

    >複数サイクル分のデータの場合、まずサイクルごとに分割して、ピークの時間がが揃うように時間を前後させて、最低値がゼロになるように引き算して、最高値が1になるように割り算して、すべてのデータの長さが揃うように前後を0で埋めないといけないです。

    これがなされているので、この点は問題ないと思います。
    ここでは、1列毎に、w-size時間分のデータが格納してあります


    たとえば元の波形がこうなっていた場合
    ```
    _____
    _| |___
    ```

    ```
    _
    _|

    __
    |

    ___

    __
    |
    _
    |_
    ```
    こんな感じに分割されて格納してあります。これらをサンプルとして学習しているので、テストデータが完璧でも、個別の波形にひっぱられるので、

    このコードは質問者さんが書きましたか?どこかから拾ってきたものであれば、多分質問者さんがやりたい事とこのコードがしている事はまったく違う可能性があります。私はエスパーではないのでこれ以上の事はわかりませんが。

    後は以下を読んで質問の内容をより精査できるといいと思います。
    https://qiita.com/kazukinagata/items/e1ef8f5fa880befa2695
  3. @poko_amu

    Questioner

    ご丁寧にありがとうございます。

    教えて頂いことをヒントにもう一度やっていることが
    合っているか考え先に進みたいと思います

    質問の仕方も勉強して
    質の良い投稿ができるように心がけます

    ありがとうございます!

Your answer might help someone💌