【検証】ChatGPTでテーブルデータの外れ値以外の異常を検知させるには

# はじめに

2023年7月にChatGPTの機能にCode Interpreterが追加され、テキスト入力だけでデータ分析が手軽に行えるようになりました。
本記事では、大規模言語モデル(LMM)をベースとした画像の異常検知手法SAA+[[参考1](#参考)]の方針を参考に、ChatGPTのチャット画面に任意のファイルをアップロードして、テキスト入力するだけでテーブルデータに含まれる複雑な異常(典型的な外れ値以外の異常)を検知できるかを検証します。

# 方針
ChatGPTで目的の出力を得るためには、プロンプト[^1]の指定内容を工夫する、複数のステップに指定を分けるといった方法が効果的である場合があります[[参考2](#参考)]。
本記事では、SAA+の方針を参考にします。SAA＋は、プロンプトを用いた画像の異常検知手法として注目[[参考3](#参考)]されており、以下の①と②の方針の内、②の方針が優れていることを示唆しています。ポイントは、はじめから抽出対象に対するある程度具体的な指定を行うことと、抽出後にもう一度具体的な指定でフィルタリングする工程を入れることです。

- **方針①：粗く取得した後、細かく抽出**
1. 汎用的な文言(「異常」「欠損」など)を入力し、異常全般を取得
2. 取得した異常から検知対象の異常を抽出

- **方針②：細かく取得し、細かく抽出、さらにフィルタリング**
1. LMMに具体的な文言(「○○性質の欠損」など)を入力し、特徴的な異常を取得
2. 取得した異常から検知対象の異常を抽出
3. 抽出した結果を具体的な条件でフィルタリング

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892617/dd1fbbd9-fdba-3ecf-4e30-3f5cc069b507.png)
**出典：SAA+[[参考1](#参考)]の図1より。方針①は図の左であり、方針②は図の右。右の方がよりピンポイントに異常を抽出できている。**

# 検証環境
言語モデル：ChatGPT (GPT-4)[[参考4](#参考)]

# 検証内容
身長と体重に関するテーブルデータ(Davis.csv[[参考5](#参考)])に対して、検証を行います。
このデータは、平均から大きく外れた値を持つレコードが含まれていますが、今回の抽出対象は、単純に極端な値を持つデータではなく、「一見正常な値のように見えるが複数の観点で見ると異常」である複雑な異常を抽出対象とします。
方針①と②をどちらも検証し、方針①ではどんな問題が発生するかを観察します。

## 方針①：粗く取得した後、細かく抽出 
1\. 粗く取得
まず、ChatGPTに「異常レコードを抽出して」というざっくりとした指示を出します。これであらゆる観点の異常が抽出されていることを期待します[^2]。

**結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892617/34853771-f8a1-d524-e59f-ba959d153050.png)

2\. 細かく抽出可能か
次に結果に関して「抽出した異常をタイプ分けして」という指示を出すと、以下の結果となりました。

**結果**
>タイプ1: 異常な値
>これは、重量や高さなどの値が他のデータと比較して非常に異常である場合です。
>タイプ2: 欠落データ（NA）
>これは、いくつかのフィールドでデータが欠落している（NA）場合です。
>以上のように、異常レコードは主に2つのタイプに分けられます。
>それぞれのタイプに応じた対応が必要とされることが多いです。

上記から、はじめに指定した「異常」は、異常全般ではなく、「異常な値」（外れ値）と「欠落データ」としてLLMに解釈されていることが示唆されます。

今回は、「一見正常な値のように見えるが複数の観点で見ると異常」であるデータを対象としたので、この段階で抽出できていないことがわかります。


## 方針②：細かく取得し、細かく抽出、さらにフィルタリング
1\. 細かく取得
具体的な異常の指示を出すために、まず異常のタイプとしてどのようなものがあるかをChatGPTに聞き、以下の出力を得ました。

**結果**
> タイプ1: 異常な値
> タイプ2: 欠落データ（NA）
> タイプ3: 一貫性・整合性のないデータ
> タイプ4: 外れ値（Outliers）
> タイプ5: テンポラル（時間的）な異常
> タイプ6: テキスト・カテゴリカルデータの異常
> **タイプ7: 複合異常**
> タイプ8: コンテクスト依存の異常
> タイプ9: 欠損パターンの異常

2\. 細かく抽出
今回の目標は、「タイプ7: 複合異常」に該当しそうです。以下のプロンプトを入力します。

- 「複合的な異常の検知方法の種類」
- 「上記の手法を用いて異常度が高いレコードを抽出してください」

**結果**
以下の3つの手法のスコア平均が高いレコードが出力されました。
>・マハラノビスの距離
>・Isolation Forest
>・One-Class SVM

「複合的な異常を検知する手法」を用いているため、ここで抽出完了となると思われるのですが、スコア上位を観察すると、以下のような意図していないはずの外れ値の異常が含まれています。これは、「複合的な異常を検知する手法」は一般に「複合的な異常のみを検知する手法」とは異なるからだと考えられます。この後もう一度フィルタリングする必要性はこのあたりにありそうです。

>インデックス○○:
>体重（weight）が＜データ平均より高い値＞と非常に高く、一方で身長（height）が＜デー>タ平均より低い値＞と非常に低い。このような極端な値の組み合わせが異常度を高めています。

3\. さらにフィルタリング
出てきた結果を複合的な異常のみに絞るように指示をすると以下のような、各カラムが平均的な値で一見すると異常とはわかりづらいようなレコードが出力されました。実際に抽出されたレコードが異常かどうかは文脈によるため、正しく検知できたとは言い切れませんが、少なくともそれらしいものを出力することはできているように思われます(結果)。

>インデックス○○:
>身長（height）が＜データ平均に近い値＞で、再測定の身長（repht）が＜データ平均に近い値＞と、身長に比べて体重（weight）が＜データ平均に近い値＞と低い。この不均衡が異常度を高めています。

**結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892617/619425f3-dc28-8419-5d84-89179ad7b2e3.png)

# まとめ
今回の検証を通して、テーブルデータに対しても「方針②：細かく取得し、細かく抽出、さらにフィルタリング」が有効である可能性が示唆されました。

今回の検証では、5列200行程度の小さくて解釈しやすいデータセットを用いており、より大きくて複雑なデータセットでも今回の検証結果が活かされるとは限りませんが、一つの結果としてLLMを用いたデータ分析の参考にしていただけますと幸いです。

改善点やご質問などあれば、コメントいただければ幸いです。

# 参考
1.	Cao, Yunkang et al. “Segment Any Anomaly without Training via Hybrid Prompt Regularization.” arXiv abs/2305.10724 (2023): n. pag.
2.	ChatGPTのプロンプトエンジニアリングの例｜簡単に使えるテクニックも解説 https://n-v-l.co/blog/chatgpt-prompt-engineering
3.	【速報】次世代の外観検査！？プロンプトを駆使した異常検知 https://qiita.com/shinmura0/items/2ebe69c1a31ae1b98de2
4.	https://openai.com/gpt-4
5.	Davis Data Set https://vincentarelbundock.github.io/Rdatasets/datasets.html


[^1]: 本記事では、「プロンプト」を「自然言語によるプロンプト」という意味で用います
[^2]: ChatGPT自体の判断能力を見るために、ここではコードによる抽出はしないように指定しました（ただ、コードによる抽出をしても抽出範囲はあまり変わりませんでした）