カーネルPCAに基づく外れ値検知にサブセットのサンプリング機能を実装して計算量削減を図った話

はじめに

先の記事

tam5917.hatenablog.com

にてカーネルPCAに基づく外れ値検知を実装したが、計算量が多く使い物にならない。

そこで本記事では、

tam5917.hatenablog.com

の記事にある、データセットのサブサンプリングを組み込むことで計算量を減らしつつ、検知性能がどの程度キープされるかを簡単に確認してみた。

実装

実装は以下の通り。

ここをクリックしてコードを表示する gist.github.com

検証

サブセットのサイズは、元のデータセットの20%になるようにランダムサンプリングした。以下がそのノートブックである。

gist.github.com

KPCA(SP)がサブセットのサンプリング版である。サンプル数の多いデータセットほど時間削減の幅は大きい(mnist、optdigits、pendigits、satellite、satimage-2)。 平均するとサンプリングなしと比較して10分の1程度までにはなった。注目すべきはサブセットをランダムサンプリングしても、サンプリングを行わないKernel PCAと比べて性能の低下はそれほど大きくない(ことが多い)ということである。むしろデータセットによっては、検知性能(ROC)がサンプリング後で改善しているものもあった(ionosphereやpendigits)。もっとも改善自体は乱数の「引き」の問題であり、たまたま良くなった、なのではあるが。各データセットの外れ値の割合を考えれば、サブサンプリングしたデータはほぼnormalなデータなので、性能の低下が抑えられているともいえる。

検証その2

先と同様に、サブセットのランダムサンプリングしたサイズは元のデータセットの20%とした。 さらに、random_stateを変えることで異なるサブセットを取る。今回の実験では5セットを作ることにした。それらの上でKernel PCAをそれぞれfitする。つまり検出器は5つである。 5つの検出器のアンサンブルを考えるために、各検出器から出力される異常度スコアの「平均」「最大値」「メディアン」を取り、ROCスコアをそれぞれ計算して比較する。異常度スコアのアンサンブルにはPyODのcombinationモジュールが利用できる。比較のため、サブサンプリングをまったく行わない、フルサイズのデータセットを使った場合の検出器も用意した。これらの間でROCスコアの変動を見ようというわけである。

アンサンブル型検出器の訓練とフルサイズの検出器の訓練で、どれほど計算時間が変化するかも見ることにした。

以下がノートブックである。 gist.github.com

グラフを示しておく。図1がROCスコア (アンサンブル型は「平均」)、図2が計算時間である。横軸はそれぞれのデータセットであり、各データセットについてサブセットサンプリング&アンサンブルを青、アンサンブルなし(フルサイズ)をオレンジの棒で示している。

図1: ROCスコア

図2: 計算時間

結果を要約すると:

  • サブセットのアンサンブル型でも、フルサイズのデータセットに匹敵する性能が出るケースが多いが、依然として負けるケースもある(図1)。サブサンプリングすることで大きく性能が向上したケース(musk)もあり、興味深いが、これはデータセットの特性に由来するものだろう。
  • データセットのサンプル数が多い場合、アンサンブル型にすることでも、依然として計算時間削減の効果は大きい(図2)

サブセットのサンプリングで性能低下しがちな面をアンサンブルで補った形と言えよう。

外れ値の比率が事前に分かっていれば、検知性能を損なわずにランダムサンプリング率をさらに小さくして計算時間の削減が可能ではある。運用上は比率が事前に分かっていることはあまりないだろう。なので今回の検証も参考程度に。

議論はあるか?

実用に持っていくためにはハイパーパラメータの調整が不可欠である。以下の論文では、カーネルPCAに基づくアンサンブル型モデルを用いた異常検知に関連して、ガウスカーネルのハイパーパラメータの最適化に関して議論されている。

Nicholas Merrill, Colin C. Olson; Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR) Workshops, 2020, pp. 112-113 openaccess.thecvf.com

カーネル主成分分析による外れ値検知をPyODフォーマットで実装した

はじめに

カーネル主成分分析を用いて外れ値検知ができるので、それを実装してみたということ。

カーネル主成分分析を外れ値検知に使うとは?

以下の論文にすべて書いてある(説明の手抜き!)。

  • Heiko Hoffmann, "Kernel PCA for novelty detection," Pattern Recognition, vol.40, no.3, pp. 863-874, 2007. Link

要するに、カーネルによって一意に決まる特徴空間上でPCAを実行し、さらに特徴空間上での再構成誤差を異常度として用いる。pre-image最適化を用いて、もとの空間に逆変換してから再構成誤差を計算する方法も考えられるが、今回は上記の論文に従うことにした。

実装

PyODのインストール

pip install pyod

KPCAクラス

PyODフォーマットに従って、KPCAクラスを実装した。以下を適当なファイル名(kpca.pyなど)で保存する。

ここをクリックしてコードを表示する gist.github.com

実装はscikit-learnのKernelPCAをラップする形である。

Toy example

PyODのexampleスクリプトを改変して実行した。うまく動いているように見える。 gist.github.com

ベンチマーク

PyOD付属のベンチマーク用のスクリプトを少し改変する。15のデータセット上で評価した。 外れ値検知の代表的な手法たちと比較する。

  • Angle-based Outlier Detector (ABOD)
  • k-Nearest Neighbors Detector (kNN); k = 5
  • Isolation Forest (IForest)
  • Local Outlier Factor (LOF); k=10
  • One-Class SVM (OCSVM)
  • Principal Component Analysis (PCA)
  • Kernel PCA (KPCA)

gist.github.com

Kernel PCAの性能はROC的にはまずまず。PCAと比較したときの優位性は十分に明らかとなった。Precisionはそれほど。サンプル数の多いデータセットは計算時間が特にかかることがわかる。

おわりに

PyOD使おうぜ!

Random Projection Outlyingness(RPO)に基づく外れ値検知をPyODフォーマットで実装した

はじめに

データ分布に対する各サンプル点の外れ度合いを測る指標として、Random Projection Outlyingness(RPO)が存在する。今回はRPOに基づく外れ値検知をPythonで実装したので、紹介する。

Random Projection Outlyingnessとはなにか?

depthについて

David Donoho先生らによる、以下の論文を参考にする。

David L. Donoho and Miriam Gasko, "Breakdown Properties of Location Estimates Based on Halfspace Depth and Projected Outlyingness," vol.20, no.4, pp.1803-1827, The Annals of Statistics, 1992.

projecteuclid.org

論文タイトルにも含まれている「depth」という量は、Tukeyらによって提案された(ちなみTukeyはFFTアルゴリズムの開発者の一人としても有名)。ある1つのデータ点(一般には多次元ベクトル)が、手元のデータセット(同次元のベクトル集合)の「分布的な意味でどのあたりに位置しているか」を記述する量がdepthである。以下にdepthの定義を示す。1次元のdepthの定義は

\displaystyle \mathrm{depth}_{1} = \mathop{\rm min} (\; \# \{ i \mid X_i \leq x\}, \; \# \{ i \mid X_i \geq x\})

である。ここで、$X$はデータセット $X =\{X_1, X_2, \ldots, X_n\}$である。入力データ$x$の左側と右側にくるデータの数を数えて、小さいほうをとる。これを$d$次元データに拡張するには、データを1次元空間に射影し、スカラーの集合に変換して比較可能にすることで、depthが計算できる。定義は

\displaystyle \mathrm{depth}_{d} = \mathop{\rm min}\limits_{\| u \|=1} \; \# \{i \mid u^{T}X_i \geq u^{T} x \}

とする。ここで$u$が射影軸を表すベクトルであり、$u^{T}X_i$は$i$番目のデータサンプルを$u$に射影した成分(内積を取っているのでスカラー)である。$u^{T} x$が当該サンプル$x$を射影したときの成分であり、成分が$u^{T} x$より大きくなるサンプルの数は大小関係を比較しながらカウントすることができる(有限集合なので比較は有限回で終わる!)。そして$\mathrm{min} $は正規化された射影軸$u$に渡って取る(暗黙的にそのような$u$の集合は有限濃度を考えている)。$d$次元版の観点から1次元版の定義を眺めると、数直線の右向きの軸および左向きの軸への射影を考えているわけである。

上記の論文で紹介されているが、1次元空間で考えるとsample maximumとsample minimumはdepthが1である。また上位/下位四分位数に対応するのは、depthとしてほぼn/4であり、メディアンに対応するのはdepthとしてn/2である(nはサンプルサイズ)。sample maximum/minimumは分布の際(きわ)に位置しているので「一番浅く」、depth的には最小値を取る。メディアンは分布のほぼ真ん中に位置しているので「一番深く」、depth的には最大値を取る。1次元のヒストグラム(山が一つ)をイメージすると分かりやすいかもしれない。上記のdepthを使うと、ロバスト統計にとって色々と興味深い議論ができる。

outlyingnessについて

さてDonoho先生らは、多次元データであってもdepthのように1次元空間に射影して統計的な性質を議論できることに目をつけ、多次元データにおける外れ度合い(outlyingness)の議論も同様にあてはまるだろうとした。1次元空間において、中心(平均)からのデータの乖離を測るために以下の量を考えるのは素朴である(1次元のホテリング$T^{2}$法において定義される異常度の平方根に相当する)。

\displaystyle \frac{|x - \mathrm{mean}(X) |}{\mathrm{std}(X)}

ここで$\mathrm{mean}(X)$と$\mathrm{std}(X)$はそれぞれ、$X$上で計算した標本平均と標本標準偏差である。しかしながら、この量は外れ値の影響を受けやすい。そこで標本平均をメディアン(median; MED)、標本標準偏差を中央絶対偏差(median absolute deviation; MAD)に置き換えた

\displaystyle r_1(x; X) = \frac{|x - \mathrm{MED}(X) |}{\mathrm{MAD}(X)}

を外れ値にロバストなoutlyingessとして考えることができる。MEDとMADは外れ値の影響を受けにくいことが知られている。

Random Projection Outlyingness (RPO)

depthと1次元射影のことを踏まえて、$d$次元のoutlyingnessへと拡張するには

\displaystyle r_d(x; X) = \mathop{\rm max}\limits_{\| u \|=1} \;  \; r_1 \left(  u^{T}x ; \{u^{T}X\} \right)

とすればよい。ここで$u^{T}X$は$u$上に射影したデータセットを表し、$u^{T}x$は$x$を$u$上に射影したデータ点(スカラー値)を表す。$\max$を取る$u$の集合は有限であり、したがって単位超球面$S^{d-1}$上から有限個をランダムサンプリングしたうえで計算する。以上がRandom Projection Outlyingness (RPO)である。文献によっては、Stahel-Donoho outlyingnessとも呼ばれる。これを外れ値検知における異常度スコアとして利用することができるというわけである。

実装

PyODのインストール

pip install pyod

RPOクラス

PyODフォーマットに従って、RPOクラスを実装した。以下を適当なファイル名(rpo.pyなど)で保存する。

ここをクリックしてコードを表示する gist.github.com

実装はとても簡単。ちなみに中央絶対偏差の計算にはscipy.statsのmedian_abs_deviation関数を使うことができる。

Toy example

PyODのexampleを実行した。 gist.github.com

ベンチマーク

PyOD付属のベンチマーク用のスクリプトを少し改変する。15のデータセット上で評価した。 外れ値検知の代表的な手法たちと比較する。

  • Angle-based Outlier Detector (ABOD)
  • k-Nearest Neighbors Detector (kNN); k = 5
  • Isolation Forest (IForest)
  • Local Outlier Factor (LOF); k=10
  • One-Class SVM (OCSVM)
  • Random Projection Outlyingness (RPO) (今回の手法; 射影次元は500 つまり射影軸を500本保持する) gist.github.com RPOは外れ値検知性能と省計算量とを両立していることがわかる。射影次元を大きくすれば計算時間とメモリ消費量は大きくなる(射影軸の集合をたくさん保持しないといけないので)。

参考文献

  • Donoho, D. L., Gasko, M., et al. Breakdown properties of location estimates based on halfspace depth and projected outlyingness. The Annals of Statistics, 20(4):1803–1827, 1992. Link

  • Zuo, Y. et al. Projection-based depth functions and associated medians. The Annals of Statistics, 31(5):1460–1490, 2003. Link

外れ値検知手法のベンチマークを測定するスクリプトをPyODとOptunaで書いた

はじめに

外れ値検知の代表的な手法であるkNN, LOF, OC-SVM, Isolation Forestについて、ベンチマークを測定するスクリプトを作成した。 各手法は外れ値検知ライブラリのPyODに実装されており、今回はOptunaによるハイパーパラメータ探索も入れている。

PyODのベンチマークについては公式にもドキュメントが用意されているので、一読を勧める。 pyod.readthedocs.io

準備

PyODはpipでインストール可能である。

pip install pyod

また、以下のgithubレポジトリからベンチマーク用のデータをダウンロードしておく。 github.com

なおベンチマーク用データ自体は以下のサイトから入手可能である。種類はたくさんあるが、今回はそこから16種類のデータセットを利用する。 odds.cs.stonybrook.edu

スクリプトたち

ベンチマークスクリプトと同じ階層に dataディレクトリを作成しておき、ベンチマーク用データはdataディレクトリ以下にまとめて置いておくこと。

スクリプトはPyLint的に10.00(満点)になるように作ったが、可読性はいまいちかもしれない。

trial数やハイパーパラメータの探索範囲などはかなり適当に決めている。

おわりに

PyOD使おうぜ!(n回目)

Optunaもいいぞ!

Rapid Distance-Based Outlier Detection via Sampling (NIPS 2013) に基づく外れ値検知手法をPyODフォーマットで実装した

はじめに

"Rapid Distance-Based Outlier Detection via Sampling" という論文の中で提案された手法を、外れ値検知のためのPythonライブラリ PyOD のフォーマットに落とし込んで、手軽に使えるように実装したということ。併せてベンチマークデータを用いて性能を評価した。

論文

論文は以下から読むことができる。

Sugiyama, M., Borgwardt, K. M., "Rapid Distance-Based Outlier Detection via Sampling," Advances in Neural Information Processing Systems (NIPS 2013), 467-475, 2013.

論文著者のSugiyama氏による解説資料(PDF)はこちらから。

Sugiyama氏によるR および C 言語による実装例はこちらから。

手法の概要

データセット全体から一度だけサンプリングしてサブセットを作り、保持する(再サンプリングは行わない)。 新規入力データの異常スコアは、サブセット上の最近傍サンプルとの距離を求めることで計算される。

サブセットのサイズを十分に小さくしても、 データセット全体を用いる他の外れ値検知手法と匹敵する検知精度を得ることができる。 かつ計算時間を大きく減らせるのでハッピー。

実装したコード

PyODのBaseDetectorクラスを継承し、Samplingクラスを実装した。

コードを表示する gist.github.com

引数の意味は以下の通りである。

引数 説明
subset_size サンプリングするサブセットのサイズを指定する。int型ならばサブセットのサンプル数、float型ならば全体に対するサブセットの比率
metric 距離を指定する。sklearn準拠。
metric_params metricのキーワード引数
random_state 乱数の種 (int) or RandomState インスタンス
contamination 外れ値の(想定)比率

デモンストレーション

準備

PyODはpipコマンドでインストールできる。

pip install pyod

上記リンクにあるコードをsampling.pyなど適当な名前で保存する。

ノートブックその1

実行例を以下のnotebookに示す。サブセットのサイズは20にした。

gist.github.com

ノートブックその2

PyOD付属のベンチマーク用のスクリプトを少し改変する。件の論文にも比較手法として掲載されている外れ値検知の各手法と比較する。

  • Angle-based Outlier Detector (ABOD)
  • k-Nearest Neighbors Detector (kNN); k = 5
  • Isolation Forest (IForest)
  • Local Outlier Factor (LOF); k=10
  • One-Class SVM (OCSVM)
  • Sampling (今回の手法)

データセットの種類は16種類である。サンプリングにおけるサブセットのサイズは50で固定した(デモンストレーションなので50という数字に特に意味はない)。 ベンチマークデータは以下のgithubリポジトリからダウンロードして使う。

https://github.com/yzhao062/pyod/tree/master/notebooks

gist.github.com

"Sampling"の実行時間はとても短いが、外れ値検知の性能は他の手法と匹敵していることが分かる。

おわりに

本記事では"Rapid Distance-Based Outlier Detection via Sampling"において提案された外れ値検知手法を実装し、その検知性能を評価した。

学習データ全体からサブセットを一度だけサンプリングして保持し、以降はサブセット上で最近傍距離を求めるというシンプルな手法である。 PyOD付属のベンチマークデータを用いた外れ値検知実験の結果から、サンプリングにより検知精度を大きく落とすことなく、実行時間を大幅に減らせることを確かめた。

学習データ量が十分にあり、かつ外れ値がある程度含まれている場合には有望な手法のひとつだろう。何より実装が簡単である。学習データに外れ値がまったく含まれていない場合にも、ある程度は動くのではと考える(実験していないので推測)。

selected.elの設定 2022

はじめに

以前、selected.elの設定を記事にしたことがあった。

tam5917.hatenablog.com

今回はその設定を一部見直し、新しい関数に置き換えた部分があるので、それをまとめておこうというのが主旨である。 selected.el自体は以下の記事を読むのがよい。

qiita.com

設定

以下にコードを示す。クリックで展開する。requireされているパッケージは適当に探せばすぐに見つかるものばかりである。

コードを表示する

;; http://github.com/Kungsgeten/selected.el
(require 'selected)

;; https://github.com/minad/consult
(require 'consult)

;; https://github.com/emacsorphanage/anzu
(require 'anzu)

;; https://github.com/victorhge/iedit
(require 'iedit)

;; https://github.com/magnars/multiple-cursors.el
(require 'multiple-cursors)

;; https://github.com/xuchunyang/osx-dictionary.el
(require 'osx-dictionary)

;; http://www.emacswiki.org/emacs/download/replace-from-region.el
(require 'replace-from-region)

;; http://github.com/Malabarba/emacs-google-this
(require 'google-this)

;; https://github.com/remyferre/comment-dwim-2
(require 'comment-dwim-2)

;; https://gist.github.com/tam17aki/be6445e3b2b28a70ccce3546d2963680
(require 'consult-selected)

;; https://gist.github.com/tam17aki/f5046e9381436e783df385b42b11125b
(require 'consult-thing-at-point)

;; 参考 http://blog.fujimisakari.com/elisp_useful_for_programming/
(defun region-to-single-quote ()
  (interactive)
  (quote-formater "'%s'" "^\\(\"\\).*" ".*\\(\"\\)$"))
(defun region-to-double-quote ()
  (interactive)
  (quote-formater "\"%s\"" "^\\('\\).*" ".*\\('\\)$"))
(defun region-to-bracket ()
  (interactive)
  (quote-formater "\(%s\)" "^\\(\\[\\).*" ".*\\(\\]\\)$"))
(defun region-to-square-bracket ()
  (interactive)
  (quote-formater "\[%s\]" "^\\(\(\\).*" ".*\\(\)\\)$"))
(defun region-to-brace ()
  (interactive)
  (quote-formater "\%s\]" "^\\(\(\\).*" ".*\\(\)\\)$"))
(defun quote-formater (quote-format re-prefix re-suffix)
  (if mark-active
      (let* ((region-text (buffer-substring-no-properties
                           (region-beginning) (region-end)))
             (replace-func
              (lambda (re target-text)
                (replace-regexp-in-string re "" target-text nil nil 1)))
             (text (funcall replace-func re-suffix
                            (funcall replace-func re-prefix region-text))))
        (delete-region (region-beginning) (region-end))
        (insert (format quote-format text)))
    (error "Not Region selection")))

(defun my:google-this ()
  "検索確認をスキップして直接検索実行"
  (interactive)
  (google-this (current-word) t))

(setq google-this-location-suffix "co.jp")

(when (require 'selected nil t)

  ;; コメントアウト・アンコメントアウト
  (define-key selected-keymap (kbd ";") #'comment-dwim-2)

  ;; 選択した関数のヘルプを表示
  (define-key selected-keymap (kbd "f") #'describe-function)

  ;; 選択した変数のヘルプを表示
  (define-key selected-keymap (kbd "v") #'describe-variable)

  ;; 選択したシンボルのヘルプを表示
  (define-key selected-keymap (kbd "y") #'describe-symbol)

  ;; 辞書を引く (Mac限定)
  (define-key selected-keymap (kbd "d") #'osx-dictionary-search-pointer)

  ;; 置換関連
  (define-key selected-keymap (kbd "q") #'query-replace-from-region)
  (define-key selected-keymap (kbd "Q") #'anzu-query-replace)

  ;; リージョンの文字数や単語数をカウント
  (define-key selected-keymap (kbd "=") #'count-words-region)

  ;; 複数カーソルによるマーク
  (define-key selected-keymap (kbd "A") #'mc/mark-all-like-this)
  (define-key selected-keymap (kbd "n") #'mc/mark-next-like-this)
  (define-key selected-keymap (kbd "p") #'mc/mark-previous-like-this)
  (define-key selected-keymap (kbd "u") #'mc/unmark-next-like-this)
  (define-key selected-keymap (kbd "U") #'mc/unmark-previous-like-this)
  (define-key selected-keymap (kbd "s") #'mc/skip-to-next-like-this)
  (define-key selected-keymap (kbd "S") #'mc/skip-to-previous-like-this)

  ;; 同時編集 iedit
  ;; デフォルトではバッファ全体にカーソルが分身して編集可能になるので、
  ;; ナローイングを適宜用いる
  (define-key selected-keymap (kbd "i") #'iedit-mode)

  ;; 参考 http://blog.fujimisakari.com/elisp_useful_for_programming/
  ;; リージョンをシングルクオートで囲う
  (define-key selected-keymap (kbd "\'") #'region-to-single-quote)

  ;; リージョンをダブルクオートで囲う
  (define-key selected-keymap (kbd "\"") #'region-to-double-quote)

  ;; リージョンをブラケット(カッコ)で囲う
  (define-key selected-keymap (kbd "(") #'region-to-bracket)

  ;; リージョンをカギカッコで囲う
  (define-key selected-keymap (kbd "[") #'region-to-square-bracket)

  ;; consult-line
  (define-key selected-keymap (kbd "w") #'consult-line-thing-at-point)

  ;; google 検索
  (define-key selected-keymap (kbd "g") #'my:google-this)

  ;; consultインターフェイスで選択
  (define-key selected-keymap (kbd "l") #'consult-selected)

  ;; 有効化
  (selected-global-mode 1))

主な変更点

  • counselの削除
  • swiperの削除
  • consultの導入(consult-apropos, consult-selected, consult-line-thing-at-point

consult-selectedconsult-line-thing-at-pointは新たに書いた関数なので、以下で詳しく説明する。

consult系の関数群の導入

takaxp氏により、helm-selected.elcounsel-selected.elが書かれていたので、これのconsult用の代替ライブラリとしてconsult-selected.elを書いた。

さらに、選択されたリージョンを元にconsult-lineを発動させるために、consult-thing-at-point.elを書いた。

このライブラリにより、consult-line-thing-at-pointという関数が提供されるので、これを適当なキーにバインドして使うことができる。

おわりに

selected.elは便利なので、利用を勧めたい。

consult-ripgrepの検索対象を現在開いているバッファたちに限定するには

以下の関数を使う(consult-ripgrep-multi-file)。通常のconsult-ripgrepはあるディレクトリ以下の全ファイルが検索対象になるが、今回は「現在開いているバッファ(に対応するファイルたち)」に検索対象を限定したということ(串刺し検索)。

consult-line-multihelm-swoopの代替に相当する。

検索対象から外したい*scratch*バッファや*Messages*バッファ の除外にはido-ignore-buffersで指定される正規表現を利用した。

;; perspective.el内の関数を参考にした
(defun consult--make-ignore-buffer-rx ()
  (defvar ido-ignore-buffers)
  (if ido-ignore-buffers
      (rx-to-string (append (list 'or)
                            (mapcar (lambda (rx) `(regexp ,rx))
                                    ido-ignore-buffers)))
    "$^"))

(defun consult--get-curret-buffer-list ()
  (let (buffer-mode-matches)
    (mapc (lambda (buf)
            (when (get-buffer buf)
              (with-current-buffer (get-buffer buf)
                (if (and (buffer-live-p buf)
                         (not (string-match-p (consult--make-ignore-buffer-rx)
                                              (buffer-name buf))))
                    (add-to-list 'buffer-mode-matches (buffer-file-name buf))))))
          (buffer-list))
    buffer-mode-matches))

(defun consult-ripgrep-multi-files ()
  "Call `consult-ripgrep' for the current buffers (multiple files)."
  (interactive)
  (let* ((consult-project-function (lambda (x) nil))
         (files (mapconcat
                 (lambda (x) (concat (shell-quote-argument x)))
                 (consult--get-curret-buffer-list) " "))
         (consult-ripgrep-args
          (concat "rg "
                  "--null "
                  "--line-buffered "
                  "--color=never "
                  "--line-number "
                  "--smart-case "
                  "--no-heading "
                  "--max-columns=1000 "
                  "--max-columns-preview "
                  "--with-filename "
                  files)))
    (consult-ripgrep)))