世界を動かす技術を、日本語で。

不確実

概要

  • 現代のソフトウェア開発 では、「確信」と「不確実性」のバランスが重要課題
  • Uncertain<T> の導入により、プログラムで不確実性を直接扱う方法を解説
  • 確率分布やモンテカルロ法 を使った実践的なコード例を紹介
  • パフォーマンスと精度のトレードオフ、現実的な導入ステップも提案
  • 不確実性を認める設計思想 が、より賢いソフトウェアにつながる主張

人はなぜ「確信」に惹かれるのか

  • 人間社会 では「自信」が魅力となりやすい傾向
  • ソフトウェア開発 では、経験を積むほど「it depends」と言わざるを得ない現実
  • 個人ブランディング において、ニュアンスよりも断言が注目されやすい現状
  • 「正しさ」よりも「確信」が求められる 風潮
  • 現場の会話 で「let’s ship it and iterate」や「that won’t scale」が多用される理由

プログラムにおける「不確実性」の扱い

  • GPS座標 など多くのデータは本質的に「ノイズ」を含む
  • Bool型 などの従来の型では「確率的な不確実性」を表現できない問題
  • currentLocation.distance(to: target) < 100 のようなコードは、曖昧さを反映できない
  • CLLocationのhorizontalAccuracy など、実際は「だいたい」の値
  • 現行のプログラミングモデル は「波動関数を早期に収束させてしまう」比喩

Uncertain<T>型による新しい抽象化

  • Uncertain<T> は「不確実性」を型システムに組み込む発想

  • University of WashingtonとMicrosoft Research による論文「Uncertain<T>: A First-Order Type for Uncertain Data」が起源

  • C#実装 だが、Swiftへの移植も容易

  • Uncertain<CLLocation>.from(currentLocation) のような使い方で「不確実な値」を生成

  • 比較演算子 もUncertain<Bool>を返し、確率として扱える

    • 例: nearbyEvidence.probability(exceeds: 0.95) で95%信頼度を判定
  • 計算グラフ を構築し、必要な時だけサンプリング

  • Sequential Probability Ratio Testing (SPRT) でサンプル数を自動調整

    • 単純な比較なら十数回、複雑な計算なら数百回サンプリング

モンテカルロ法で直感的に理解

  • 確率計算 が難しい場合、 モンテカルロ法 で「試行回数」を増やし期待値を算出
  • スロットマシン の実装例で、理論値ではなく「実際に回してみる」ことで期待値を推定
  • 例: Uncertain<Int> { SlotMachine.spin() }.expectedValue(sampleCount: 10_000)

多様な確率分布と統計演算

  • Uncertain<T> は様々な確率分布を標準搭載

    • 正規分布(センサーのノイズ)
    • ベルヌーイ分布(ユーザー行動)
    • 指数分布(ネットワーク遅延)
    • 混合分布(カフェ来店時間など)
  • 統計操作 も簡単

    • 期待値、標準偏差、信頼区間、歪度、尖度、エントロピー、最頻値など
    • cdf で累積確率も取得可能
  • サンプリング数 は用途に応じて調整可能

実践的な導入ステップと注意点

  • 段階的移行 が推奨、重要な経路からUncertain<T>型を導入
  • 拡張メソッド で既存型に「uncertain」プロパティを追加可能
  • UI用途は高速・低精度、重要判定は高精度・多サンプル で使い分け
  • 計算コスト を意識し、パフォーマンスに注意

不確実性を受け入れる設計思想

  • 「不確実性を消す」ではなく「受け入れて扱う」ことがゴール
  • 現実世界 では「不確実性」こそが唯一の確実
  • よりスマートなソフトウェア を目指すための道筋

まとめ

  • Uncertain<T> は「確率的な不確実性」をプログラムで自然に扱うための強力な抽象化
  • 段階的な導入パフォーマンス配慮 が現実的なアプローチ
  • 人間の認知や現実世界に即した設計 が、信頼性と使いやすさを両立する鍵

Hackerたちの意見

これは異なる変数間の共分散を扱ってるの?例えば、距離を測ってる対象の位置にも誤差があるだろうし、それが自分の位置と相関してるかもしれないよね(例えば、同じ時間に動作してる別のGPSからのデータだったら)。確かに、型システムの中で単変量モデルは役立つかもしれないけど、共分散を扱えるとさらに強力で正確になると思う。

量子力学を正しくモデル化するには、絡み合った変数のセットに複素数値の波動関数を関連付ける必要があるよ。

共分散を追跡する必要があるなら、Pythonでgvarを使ってみるといいかも。 https://gvar.readthedocs.io/en/latest/

小さな注意点だけど、GPSは特定の条件、つまり開けた空や長時間のフィックスの時にだけ円形の不確実性でうまく近似されるんだ。完全な不確実性モデルはもっと複雑だから、エラーを測定する方法がたくさんあるんだよね。これは、フィックスを最初から点の位置として扱わなくなるような多くの状況でも重要になる。具体的な例を挙げると、自動運転車は、位置特定の不確実性が非円形のマルチパス効果に支配される状況に直面することがある。これを突き詰めていくと、最終的にはパーティクルフィルターやそれに似たものを再発明することになるよ。

車両のGPSは、通常、スピードメーターやコンパス、マップに載ってる道路のどれかにいるという前提など、たくさんの追加センサーや仮定によって強化されてるんだ。最後に電源を入れてから位置が変わってないと仮定できるから、早くフィックスが取れるのも大きいよね。

たぶん、Uncertainがデフォルトで、ほんとに確信があるときだけタイプをcertain Tとして注釈を付けるべきだよね。 ;)

Optionalの補完だね。

物理的な測定だけの話だね。お金みたいなものは、かなり正確に、時には小数点以下のセントまで確信を持っているべきだよ。現代のFortranライブラリのいくつかでも、似たようなアプローチが実装されているみたい。

機械工学の図面で職人とコミュニケーションを取るためには、許容範囲を使うんだ。例えば、10cm +8mm/-3mmみたいに、許容される範囲を示す。GPSに「もう着いた?」みたいなことを言うときは、エラーの方向や不確実性のどの方向が良いか悪いかを理解してるべきだと思う。

この表記について気になっているのは、時々「限界を超えられない」という意味になったり、時々「限界を超えるのは10%の確率である」という意味になったりすることなんだよね。

もっと「近似的」な考え方をしているなら(実験物理学のラボやその学部の授業でよくある、実世界の再サンプリングされた歪んだ分布を得るためのシミュレーションによる伝播とは対照的に)、小さな誤差のための誤差伝播(https://en.wikipedia.org/wiki/Propagation_of_uncertainty)の簡略化ができるよ。それから「ルート」誤差を「下流誤差」に変換するのは、単純な連鎖律の微積分の話だね。(毎週か二週間に一度は使っているNimライブラリがあるよ。タイミングを測るときにね。)データや測定を誤差が小さい領域に持っていくには、ちょっとした「工夫」が必要だから、現実のデータに見られる長い/太い/重い尾を持つ奇妙な形の分布に対しては、こういう不確実性を使う方がいいと思う。{ もしコストが他の面で邪魔にならないなら、という前提だけどね。記事のシニアエンジニアの言う通り。 }

これはこのクラシックなファンクショナルパールに密接に関連しているね:https://web.engr.oregonstate.edu/~erwig/papers/PFP_JFP06.pdf すごくクールだよ!僕はHaskellの入門コースを始めるときに、モンティ・ホール問題を確率モナドを使ってデモするんだ。2つの戦略で勝つ確率を分数で正確に出すために有理数を使うんだよ。

この概念は「区間算術」という名前で過去に何度も行われてきたね。Boostにもあるし[1]、flintにもあるよ[2]。本当に興味深いのは、何度も再発明されているのに、なぜもっと一般的にならないのかってこと。実際に使ってみて、悪いアイデアだと判断した人たちと話してみたいな(もしそういう人がいればだけど)。[1]: https://www.boost.org/doc/libs/1_89_0/libs/numeric/interval/... [2]: https://arblib.org/

区間算術は、定数倍遅くなるだけだけど、すべてのステップで簡略化できるかもしれない。数に対するすべての操作には、区間に対する唯一の最も正確な等価操作があるんだ。なぜなら、ガロア接続があるからね。でも、最も正確な方法で数の集合を区間として表現できるからといって、その表現が正確だとは限らない。ここでサンプリングされる計算グラフはずっと遅いけど、正確であることができるよ。毎ステップで精度を失う抽象的なドメインは必要ないんだ。

区間算術は、区間の端点が正確に表現できるときに直感的に理解しやすいよ。でも、それをどうやってやるかは明らかじゃないね。

記事にはこう書いてあるね、> 「内部では、UncertainはRayleigh分布を使ってGPSの不確実性をモデル化している。」Rayleigh分布は、単に均一にランダムな分布の間にある区間ではないことが明らかだよ。通常の区間算術は役に立たない。なぜなら、その均一なランダム分布は現実世界の良いモデルではないから。例えば、君がリンクしたBoostライブラリに頼んで、(-2,2)*(-2,2)を計算させてみて。結果は(-4,4)になるよ。もっと理にかなった結果は、(-2.35, 2.35)みたいなものかもしれない。-4の下限は、区間の端点である-2と2を掛け算したときにのみ達成できるもので、確率的に言えば、これらが独立したランダム変数だと仮定すると、同時にこの極端な値を達成する確率はさらに低くなるはずだよ。

不確実な値やランダム変数、区間、ファジィロジックなどを考えるのは難しいし、確実なものをモデル化する方が処理が簡単で、現実を十分にモデル化できるからね。

数年前に区間算術に関する歴史的な研究を見つけたとき、60年代にハードウェア開発者に区間算術を新しいCPUの基本設計にするように強く訴えていたグループがいたことに驚いたんだ。普通の整数や浮動小数点を使うだけでは、世界を正しくモデル化できないって言ってたんだよね。

シンプルな型(ブール値とか)を使うのはすごく簡単で、考えやすいし、欠点も明らかだよね。物理的な不確実性をモデル化するのは難しくて、ドメインごとに異なるモデルが必要になる。そういうことをしなきゃいけないと決めたら、きれいなAPIの裏にベルカーブがあるライブラリを使うより、目的に特化したモデルを使った方がずっと良いと思う。

もし正しい抽象化を選ぶことを心配しているなら、これはほぼ確実に間違った選択だと思う。

確率を計算することに重点を置いているのがすごく好き。すべてを閉じた形の数学や古典的な確率の演習に押し込むのではなくて。シミュレーションしたり、サンプリングしたり、分布と直接やり取りする方がずっと直感的だと思う。コンピュータを使うと、プロセスを実行して結果を見て、そこから推論するのが自然に感じるんだよね。

ProbabilityDistribution型と呼ぶ方が適切な気がする。概念を扱うには、もっと一般的で直感的な方法だと思う。

でも、短い名前が勝つよね。