2 ポイント 投稿者 GN⁺ 2024-05-17 | 1件のコメント | WhatsAppで共有
  • CVMアルゴリズムは、長いデータストリームにおける異なる項目数を近似し、リスト全体を保存しなくても固有項目数を推定する
  • 入力全体がメモリを超える**固有項目問題(distinct elements problem)**に焦点を当てており、重複入力の多い大規模ログやイベントストリームに適用できる
  • 限られたメモリに一部の項目だけを残し、容量がいっぱいになるたびにランダム削除を繰り返すことで、各項目が残る確率を均等にする
  • Hamlet の例では、100語分のメモリで5回の平均として3,955個を推定し、実際の固有単語数3,967個に近かった。1,000語分のメモリでは平均3,964個に改善した
  • メモリが大きいほど精度は高まり、すべての固有項目を収められるほど十分であれば100%の精度も可能

長いデータストリームで固有項目を数える

  • 目的は、項目が1つずつ入ってくる長いリストから、重複を除いた固有項目数を効率的に推定すること
  • 最も単純な方法は、これまで見たすべての項目を保存し、新しい項目が入るたびに既存リストと比較すること
    • 野生動物調査では、すでに見た動物写真のリストを確認し続ける必要がある
    • Facebookの日次ログインユーザー数のように、リストが数十億規模になると保存と比較が難しくなる
  • CVMは、Sourav Chakraborty、Vinodchandran Variyam、Kuldeep Meelの名前に由来するアルゴリズム
  • 単語、ベルトコンベア上の商品、高速道路の車両のように、項目が順番に入ってくるリストに適用できる

CVMアルゴリズムの中核アイデア

  • CVMはすべての項目を保存せず、限られたメモリに入る一部の項目だけを保持する
  • 各固有項目が最終リストに残る確率を制御するためにランダム性を使う
  • Andrew McGregorは、このアルゴリズムは非常に単純で実装しやすいため、実際の固有項目問題における基本的なアプローチにもなり得ると見ている

Hamletの例で見る動作の仕組み

  • Hamlet には合計30,557語があり、アルゴリズムはそのうち固有単語数を推定する
  • メモリが100語分のホワイトボードだと仮定すると、最初は繰り返し出てくる単語を飛ばしながら、最初の100個の固有単語を書き留める
  • 空きがなくなると、各単語ごとにコインを投げる
    • 表なら単語を保持する
    • 裏なら単語を削除する
    • この予備段階の後には、約50個の固有単語が残る

ラウンドが進むほど厳しくなる保持条件

  • Round 1では新しい単語を追加し続け、すでにリストにある単語が再び出てきたらコインを投げ、裏なら削除する
  • リストが再び100語でいっぱいになると、100回のコイントス結果に応じて約半分を削除し、Round 1が終わる
  • Round 2からは、単語が生き残るのがより難しくなる
    • 繰り返し単語が出てきたら、裏なら削除する
    • 表が出たらもう一度コインを投げ、2回目も表のときだけ保持する
  • 第3ラウンドでは表が連続3回必要で、第4ラウンドでは連続4回必要になる
  • 一般に、k番目のラウンドが終わるとき、各単語が残っている確率は1/2^kになる

推定値の計算と実験結果

  • 最終リストに残った単語数を、残っている確率で割ると、全体の固有単語数を推定できる
  • たとえば6ラウンド後に61語が残っていれば、確率1/2^6で割って3,904個という推定値を得る
  • Hamlet の実際の固有単語数は3,967個
  • メモリサイズが大きいほど、推定値は実際の値に近づく
    • 100語分のメモリで5回実行した平均推定値は3,955個
    • 1,000語分のメモリでは平均推定値が3,964個
  • Variyamらは、この手法の精度がメモリサイズに応じてスケールすることを数学的に証明した

単純だが非自明な解決策

  • CVMは、40年以上研究されてきた固有項目問題における重要な進展と評価されている
  • William Kuszmaulは、非常に基本的でよく研究された問題にも、単純だが簡単には思いつきにくい解法が残っている可能性があると見ている

1件のコメント

 
GN⁺ 2024-05-17
Hacker Newsのコメント
  • このアルゴリズムの DNF の体積カウント版の実装に、著者たちと一緒に参加した。関連記事はこちら: https://www.msoos.org/2023/09/pepin-our-probabilistic-approx...
    コードはこちら: https://github.com/meelgroup/pepin
    ときには全体時間の30%がファイル読み込みI/Oに費やされるほど、このアルゴリズムはばかげて速い。ちなみに Knuth もアルゴリズムに貢献しており、彼のノートはこちら: https://cs.stanford.edu/~knuth/papers/cvm-note.pdf
    TAOCP の作業から丸1か月を割いてこれをやっていて、想像どおり信じがたいほど優秀だった

    • 本当に興味深いし、並外れて才能のある人たちにとても関心がある。Knuth をそこまで凄いと感じた理由が知りたい。特別な瞬間があったのか、アイデアを理解する速さだったのか、それとも簡単に説明する能力だったのか気になる
    • これは間抜けに見える。かなり間抜けだが、何か見落としているのだろうか?これはカウントではなく単なる サンプリング で、実際にすべての異なる単語を数えたいなら、単純に数える場合と比べてメモリ使用量は変わらないように思える
    • もしかすると分かっているのかもしれないが、なぜ大きなカウントを優先するようにソートして、いっぱいになったら下位半分を捨てる方式にしないのか気になる。他の人には obvious かもしれないが、理由を知りたい
    • このアルゴリズムの主な用途が何なのか気になる
    • これで Knuth の次の本の刊行が遅れている責任を問う相手ができたね :)
  • このアルゴリズムは、論文でも引用されている HyperLogLog に似て見える。表/裏が連続して出るかどうかを追跡して推定値を得るという同じ洞察を使いつつ、そのアイデアを反転させ、記憶している値をコインの結果の連続性に応じて捨てる、より単純なアルゴリズムにした感じだ
    ストリーミング状況で特に効率よく動作し、誤差率はあるものの、異なる要素数を数える「カウンター」のようなものを維持できる
    HyperLogLog の利点は、ある意味でハッシュ集合のように振る舞うことだ。項目を追加し、異なる項目数を数え、重要なことに2つの HLL をマージして和集合を作れる。それでいて、数十億項目の集合でもメモリは数KBに固定される。分散データストアでは Elasticsearch/OpenSearch の cardinality agg、Redis/Redict の PFADD/PFMERGE/PFCOUNT がこのテクニックを使っている
    CVM アルゴリズムが HLL と正確にどう比較されるのかはよく分からないが、Knuth のレビューを受けていて、学部生でも簡単に実装できるというのだから、かなり良いアルゴリズムなのだと思う

    • HLL は2つの HLL の和集合と積集合の両方を推定できるので、ジョインのカーディナリティ推定にも使える
      http://oertl.github.io/hyperloglog-sketch-estimation-paper/
    • これらのデータ構造もマージできる。マージする2つのインスタンスの「ラウンド」が違う場合、早いラウンドにいる方をラウンド差の分だけ進めればよい。つまりランダムに半分を捨てる。その後、一方のリストの値をもう一方のリストに入れ、重複は無視する。結果が大きすぎる場合はランダムに半分を捨て、ラウンド番号を上げればよい
      以前の職場でまさにこのアルゴリズムを実装したが、各値の横に、その値が出現した回数の推定値も一緒に保存していた。おかげで、最も頻繁に出る値のおおよそのリストと、各値の推定カウントを作れた
    • 昔の学校での記憶をたどると気になるのだが、ここでいう HLL と CVM は、以前学んだ reservoir sampling とどんな関係があるのだろう?
      以前病院で働いていたとき、DAT テープに保存されたレコードの小さな部分集合を作るために reservoir sampling を使ったことがある
  • 論文を読むのに、ブログ記事を読むのとほぼ同じくらいの時間しかかからず、論文のほうが有益だった
    https://arxiv.org/pdf/2301.10191
    ストリームから現れた要素集合のカーディナリティを推定する内容。アルゴリズムがあまりに単純なので、論文を読みながら自分でコーディングして遊べる
    著者らは、このアルゴリズムの想定読者と目的が学部生と教科書だと明確に述べている

    • 論文の副題である “An Algorithm for the (Text) Book” は、単純さと美しさがあまりに優雅で「The Book から出てきた」証明のようだ、という Paul Erdős の有名な表現を指しているように思える
      Knuth が自ら査読したのだから、このアルゴリズムはそういう種類のものだと評したのかもしれない。だとすれば、著者らがそれをタイトルに入れたのは謙虚ではない自慢に見えるが、十分に自慢に値する
      もともとはこの表現を Knuth のものだと記憶していたが、自分の記憶違いだった
    • ブログ記事は半分以上が水増しだった。アルゴリズムが単純すぎて長いブログ記事にしにくいというのは、まあ良いことではある
    • 論文のほうがブログ記事より良いという点には同意するが、CVM 論文への批判を一つ挙げるなら、終了条件を置いている点だ。別スレッドに出ていた Knuth の CVM ノートでは、リザーバを半分に減らす段階で空きがさらにできるよう、単にループを置いている
      https://en.wikipedia.org/wiki/Up_tack を説明するより、単にループを書くほうが面倒が少なそうに見える。[1]
      [1] https://news.ycombinator.com/item?id=40388878
    • 昔はコンピュータサイエンスをやっていたのに、頭がつるつるになってしまったのか、これは必要以上にややこしく見える
      まず contradiction の扱いが、単にエラーやパニックのようなのに、なぜそう表現したのか分からない。それに 1..m という前提が分かりにくい。サイズを事前に知っている必要があるのかどうか確信が持てなかったが、さらに見るとそうではなさそうだ。しきい値を選び、ストリームサイズに応じて確率が変わるのに、アルゴリズムの説明は単一の出力を持つかのようになっていて混乱する
      Chernoff 境界と delta/epsilon も論文ではまったく説明されておらず、さらに混乱した。Go で実装してみたコードはこちら: https://github.com/betamos/distinct
      しきい値まわりを helper に切り出すほうが、誤ってメモリを割り当てすぎるよりずっと筋が通る。信頼度や誤差率を推定するメソッドもあるべきだと思う。ストリームサイズを事前に知っている人はいないので、進行中にこの値を更新するほうが自然だ
    • 「学部生と教科書向け」というのが、彼らが使うのに十分単純だという意味ではなく、本当にそこにしか有用でないという意味なら、なぜ専門家には有用でなく学部生にだけ有用なのか説明してほしい
  • 論文のテーマを考えると、脚注が特に魅力的だ
    著者らは、昔からの著者名アルファベット順の慣例ではなくランダムな順序を選び、r⃝ で表示したとのこと。ランダム化の公開検証可能な記録はこちら: https://www.aeaweb.org/journals/policies/random-author-order...
    [0]: https://arxiv.org/pdf/2301.10191

  • アルゴリズムの説明が間違っているように思わないか?
    「リストにすでにある単語に遭遇したらもう一度コインを投げ、裏ならその単語を消す」という説明どおりに「リストにあるか確認してから削除」を実装すると、約20回繰り返され、推定値が 772800512 のようなとんでもないものになる
    逆に、単語を先に保存してから同じ単語を削除すると、実際のユニーク単語数 7233 に近い 7240 が出る。つまり説明上は順序が重要なのに、誤って伝わっているようだ

    • 同じ問題に遭遇した。Quanta Magazine の説明だけを見て arxiv 論文を読まずに実装すると、いつも 461746372167462146216468796214962164 のような推定値になった
      論文を読んだ後は正しい推定値が出るようになり、問題は小さなelse 一つだった。Quanta の説明は「リストになければ追加し、そうでなければ確率に応じて削除する」と読めるが、正しい実装では、追加したかどうかに関係なく、その後で確率条件を適用しなければならない
    • ちょうど解いていて、他の人も同じ問題に遭遇したのか見に来たところだが、その通り。説明どおりだと間違いで、各ラウンドで新しい値を追加した後に確率的に枝刈りし、メモリが上限に達したら集合全体からランダムに半分を取り除く、という形で実装する必要がある
  • 集合のユニーク要素数を推定することと、集合のユニーク要素数を数えることはまったく別物だ。素晴らしい方法だが、タイトルはいまいちだ

    • 両者はそこまで違わない。現実世界のあらゆる数え方にはゼロではない誤差率があるので、ほとんどの文脈では二つの用語は相互に置き換えて使われる
      たとえば選挙で「票を数える」と言うが、接戦なら「再集計」を行い、元のカウントとわずかに異なる数字が出ることは十分に予想される。ならば票数えも実際には票の推定であり、再集計はより狭い誤差境界を持つ推定にすぎない
      「countless stones」の神話(https://en.wikipedia.org/wiki/Countless_stones)も、どれほど大きく硬く静的な立石のようなものでさえ、正しく数えたと過信はできないという民間的な注意喚起のように感じる
      数えることが推定ではない場合は、数学的な状況くらいに限られる。すべての項目を漏れなく扱い、ある項目の同一性を別のものと混同していないと保証できる場合だ
    • 比較的小さな数ではその通り。しかし非常に大きな数では、普通は推定が数えることと同等に扱われ、結果も整数ではなく科学的記数法、つまり浮動小数点のように表されることがある
      たとえばモルは整数だが、その値は近似的にしか知られておらず、正確な値を気にする人もいない
    • これは estimation ではなくapproximation
  • こういう 枠の外で考える 例が本当に好きです。仕事上でも自分があまり得意でない部分なので、なおさらです。問題を解く正しい方法を学ぶだけでなく、抱えている問題をより簡単に、時には解けるものにしてくれる問いを見つけ出す過程が重要です
    ここでは「正確な数は必要なく、定義されたパラメータの範囲内で確率的な範囲を定めればよい」という問いが核心です。別の問題には別の問いがあるはずです。こうした例を十分に見れば、その思考過程を内面化して適切に適用できるようになることを願っています

    • 公平に言えば、これは大学の研究チームがやったことです。文字どおり、一日中ひとつのテーマを科学的方法で繰り返し検討できる人たちのチームです
      大企業で、同じくらい優秀なエンジニアたちと一日中ホワイトボードの前に座っていることに対して給料をもらえるなら、世間の人には「枠の外の解法」に見えるものをきっと生み出せるでしょう
      しかし私たちの大半は JIRA の工場ラインで働くことに対して給料をもらっているので、ひとつの問題だけに取り組んで実験する時間は限られています
    • 一般には 水平思考 と呼ばれるものだと思います。Edward de Bono が関連する本を何冊か書いていて、興味深いかもしれません
  • 「Facebook で毎日ログインする異なるユーザー数を数えたい。しかも一部のユーザーは複数のデバイスや複数の時点でログインする場合は?」という例は、このアルゴリズムが実際に役立つ状況としてはあまり適切ではない気がします
    ログインプロセスを設計する時点でこの情報が必要だと分かっているなら簡単です。各アカウントの最終ログイン日を保存し、保存されている値が現在の日付と異なる場合にだけユニークユーザーのカウンターを増やせばよいです
    たとえそうでなくても、後からデータベース上のログインイベントストリームを「再生」して分析できるでしょう。すでに数年分のデータが蓄積されている場合は別かもしれません

    • その方法では「各アカウントの最終ログイン日」を追跡する必要があるため、ユーザー数に比例したメモリが必要です。このアルゴリズムの要点は、はるかに小さく 固定されたメモリ で実行できることです
  • カウントに関連して、ストリームから 上位 k 個の項目 を見つける、効率的で実装も簡単なアルゴリズムに触れておきたいです。思ったより知られていないようです
    A Simple Algorithm for Finding Frequent Elements in Streams and Bags
    Karp, Shenker & Papadimitriou
    https://www.cs.umd.edu/~samir/498/karp.pdf

    • 「ストリームから上位 k 個の項目」という表現は、要旨の説明とは違って聞こえます。要旨では、大きなアルファベットから来る非常に長い記号列の中で、頻度が所定のしきい値より高い記号を見つけると言っています
      あなたの説明は、固定された k 個の項目を見つけ、それらが必ず最上位であることを保証するように聞こえます。要旨は、特定の値 k より大きいという条件を満たす、事前には個数が分からない項目を見つけるように聞こえます
      「最年長のユーザー 100 人を見つける」と「30 歳を超えるすべてのユーザーを見つける」の違いのように見えるのですが、私があなたの話か要旨を誤解しているのでしょうか? 英語が母語ではないので混乱しています
  • コンピュータ科学者たちが、部分集合のサイズをメモリ効率よく 推定 する方法を発明したのですね

    • より少ない回数のコイントスだけで推定値が得られるなら、速くもありそうです。異なる単語数を推定するために「本」全体を最後まで走査しなくてもよいかもしれません
    • ここでは部分集合が重要です。つまり 一意な要素の部分集合 です