---
title: "Lerf"
tags: []
published: true
---

# note: LERF

## **Paper**

1. 研究の背景

* なぜこの研究をする必要があるのか: open vocablaryのクエリを使って3D情報内のオブジェクトを検索できるようにするため
* 「いつ」「誰が」「どこ」「どのように」必要としているのかなどを掘り下げる：VLMの原理解明に役立つ可能性がる。ロボ、3Dシーンとインタラクティブなアクションをしたい人に役立つ。

2. 研究の目的

* 端的に何をするのが目的の研究なのか：NeRFの点群内でのテキストを使ったオブジェクト検索をすること。もっと広く言うと、入力（テキスト）に対しての意味的に正しいフィードバックを得られるようにすること
* 手段ではなく目的であることに注意する：手段としてはNeRFの点群にCLIPなどのモデル由来の言語情報を埋め込む

3. 既存技術との違い

* 既存技術ではなぜできなかったのか
* 「精度が低い」「速度が遅い」などではなく、その原因となっているアーキテクチャ的な問題まで特定する

| 既存技術の問題 | アーキテクチャ的な原因 |
| -- | -- |
| long-tail / abstractな言語クエリに弱い | DFFは **LSegなど、セグメンテーション用データでfine-tuneされた特徴**を3Dに蒸留するため、元のCLIPの広い言語能力が失われる |
| `"mug"` と `"handle"` のような異なる粒度を同じ場所で扱いにくい | 1つの3D点に **1つのfeatureしか持たせない**。featureが観測スケールに依存しない |
| 推論時のregion proposal系のアプローチは未知概念に弱い | まずmask / bounding regionを切ってからCLIPするので、**proposal generatorが知っている物体構造に制約される** |
| CLIPはpixel単位では教師信号となる埋め込みを生成できない | CLIPは基本的に画像/crop単位で学習されており、**pixel-aligned featureではない** |

4. アイデア

* 3で未解決の問題をどう解決したのかが1対1で書いてあるはず
  * long-tail / abstractな言語クエリに弱い→fine-tuningなしでそのままのCLIPを教師として使う
  * `"mug"` と `"handle"` のような異なる粒度を同じ場所で扱いにくい→特徴を得る際にスケールパラメータsを与えて、それで条件付けする。スケールパラメータによってはその点はmugにもhandleにもなる
  * 推論時のregion proposal系のアプローチは未知概念に弱い→位置・スケールを機械的に変化させてCLIPへ渡す。region proposalをするモデルがそもそも"handle"だけのマスクを提案してくれないということは無くなる
  * CLIPはpixel単位では教師信号となる埋め込みを生成できない→multi-scale cropをCLIPに通してfeature pyramidを作り、rayごとの教師にする。

5. 評価方法

* データセット：著者が撮影して作成したオリジナルデータセット
  * [https://huggingface.co/datasets/frinfo702-hf/LERF-OVS](<https://huggingface.co/datasets/frinfo702-hf/LERF-OVS>)
* 比較対象
  * LSeg
  * OWL-ViT
  * LERF
* 評価指標
  * Existence Detection：聞かれたものがscene中に含まれていたら"true"を返すという評価。聞いたうち何個答えられたかを指標にすると、適当に全部yesと答えた場合にスコアが高くなってしまうので、Precision–Recall curveを使って評価。四角全体を埋め尽くせているほど、「より余計な回答を少なく、求められたもののみを正しく検出できるているか」を示している
    * Precision：検出したもののうち、何割が正解か

      ![image.png](https://uploads.linear.app/06dc488b-d32d-4ab9-96b0-20990cbd6b22/9609f548-2d29-4fc6-bb40-8bccaa1f1727/aa378b04-fcf8-42ed-8388-b800d9247fb1)
    * Recall：本当に存在するもののうち、何割を検出できたか

      ![image.png](https://uploads.linear.app/06dc488b-d32d-4ab9-96b0-20990cbd6b22/a4254c93-a796-497b-8fdc-bcf8cd55ed9c/51aa6b6b-a39b-4cdc-ad38-a75a044aa10f)

![image.png](https://uploads.linear.app/06dc488b-d32d-4ab9-96b0-20990cbd6b22/c24ef3eb-809d-4b6e-9aa6-9d634066d323/e45ad7e7-6171-4aaa-b1e0-bcdb4e96558e)

* Localization：Localization Accuracy
  * LERF / LSeg：最大relevancyのpixelがGT bounding box内なら成功
  * OWL-ViT：出力が点ではなくboxなので少し違う。予測boxの中心がGT box内なら成功
* LocalizationのOverall結果：
  * LSeg 3D: 18.0%
  * OWL-ViT: 54.8%
  * LERF: 80.3%

Ref:

[LERF: Language Embedded Radiance Fields](https://www.lerf.io/)

## **Process**

以下はコードを追う際のメモで、下部にそれぞれのpathごとのtrace downをまとめている

* [note: LERF](https://linear.app/uyuthropic/document/note-lerf-371830f214d0#sampling-1afb80bc)
* [note: LERF](https://linear.app/uyuthropic/document/note-lerf-371830f214d0#how-to-create-supervision-signal-a24743ac)

フローとしてはmain.pyとかがあるわけではない。入り口が`ns-train lerf --data <data_folder>`となっている。ns-cli (nerfstudioというのがある）を起点としたスタート。フレームワーク的なものだと考えるとconfigあたりに内部設定が書いてある。

よって

https://github.com/kerrj/lerf/blob/db08d578038d884542688511bd9ad7b489a65673/lerf/lerf_config.py#L23-L30

を参照すると、pipeline定義がある。

Nerfstudioの実装は見ないので、ここは挙動だけを簡単に調べつつ、抽象化したまま扱う

```
lerf_method = MethodSpecification(
    config=TrainerConfig(
        ...
        pipeline=LERFPipelineConfig(
            datamanager=LERFDataManagerConfig(
                dataparser=NerfstudioDataParserConfig(train_split_fraction=0.99),
                train_num_rays_per_batch=4096,
                eval_num_rays_per_batch=4096,
            ),
            model=LERFModelConfig(
                ...
            ),
            network=OpenCLIPNetworkConfig(
                clip_model_type="ViT-B-16", clip_model_pretrained="laion2b_s34b_b88k", clip_n_dims=512
            ),
            #  You can swap the type of input encoder by specifying different NetworkConfigs, the one below uses OpenAI CLIP, the one above uses OpenCLIP
            # network=CLIPNetworkConfig(
            #     clip_model_type="ViT-B/16", clip_n_dims=512
            # )
```

Nerfstudioがやるのは、画像->Nerfの変換。今回本質的な点はここではないので、この変換後を取得している部分を追う

Inputから追うとこのままでは行き着くのはnerfstudioの実装なので、nerfstudioの出力を入力として追うのが良さそう

![LERF Rendering.png](https://uploads.linear.app/06dc488b-d32d-4ab9-96b0-20990cbd6b22/685598df-f7c2-48d8-b429-e29d8a0f3bef/5d66ba0a-600a-455c-a5fc-58e44c4563ed)

論文のこの図を見て、この”Network”というのが、完全にor部分的にnerfstudioなのではと考え”dino”で検索すると、dinoの（おそらく）埋め込みを取得する処理が見つかった

https://github.com/kerrj/lerf/blob/db08d578038d884542688511bd9ad7b489a65673/lerf/lerf.py#L126-L128

では、この引数の`lerf_field_outputs`を追ってみると、

https://github.com/kerrj/lerf/blob/db08d578038d884542688511bd9ad7b489a65673/lerf/lerf.py#L122

```
lerf_field_outputs = self.lerf_field.get_outputs(lerf_samples, clip_scales)
```

```
 lerf_samples: RaySamples = ray_samples._apply_fn_to_fields(gather_fn, dataclass_fn)
```

```
def gather_fn(tens):
            return torch.gather(tens, -2, best_ids.expand(*best_ids.shape[:-1], tens.shape[-1]))
```

というふうに、おそらくrayに対して計算するというプロセスの一部がここから探れそうなことがわかった。つまり、今回は入り口をrayとして捉えるのが良さそう

### sampling

* ray:  rayを飛ばす。カメラパラメータにより方向が決まる （線上に飛び飛びのsampling用の点があり、その点を(x,y,z)+カメラ情報でparameterizeする）
* point on the ray (with scale): ray上に飛び飛びでsamplingする。この時点で(x,y,z)が決まり、この時のカメラからの点の距離に応じてscaleが設定される。CLIPに渡す際のcropの大きさがこれで決まる
* RGB+features: NeRF studioがカメラパラメータからそのpointごとの点群情報を返す（色・密度）。またCLIP, DINOにもその点や点群の情報を渡し埋め込みを得る
* language field: volume rendering; 1本のray上にある複数の3D点の情報を、手前から奥へ重み付けして1つのpixel値にまとめる. ただ、言語情報も含んでいる点が特殊 (pixel+language embedding的な)
* loss: 教師信号とのlossを計算

![image.png](https://uploads.linear.app/06dc488b-d32d-4ab9-96b0-20990cbd6b22/6ee21032-dda4-446d-a9f2-613c83c0e637/471592a8-e7b3-48b5-9a95-5cd128ca741a)

### how to create supervision signal

* image: samplingに使った視点と同じ画像を使い、そこを複数粒度（画像サイズ）で切り出す
* CLIP: それぞれの画像をCLIPに入れて埋め込みを得る
* Language field: ここまでで、image+camera+embeddingが粒度ごとに得られたので、それぞれに重みをつけた線形和を取り、一つのlanguage field (= image + camera + embedding)にする
* loss: sampling dataとのlossを計算

![image.png](https://uploads.linear.app/06dc488b-d32d-4ab9-96b0-20990cbd6b22/1489ff97-031e-4b6c-87a8-0d98150b9115/c6c7ccd3-4739-4a85-a23b-cd4210212d77)
