んだ日記

ndaDayoの技術日記です

『ビジネス課題を解決する技術』第1章 ビジネス課題を解決する技術

こんにちは!!んだです!

今日も今日とて読書メモです!!

今回はこれの第1章!!

1章ではビジネス課題をデータサイエンスで解くための「型」を覚えます。

ここで言う型というのは、どんな課題が来ても同じ手順で取りかかれる、考え方の枠組みのことです。

例えば「売上を伸ばしたい」「お客さんが離れていくのを止めたい」みたいなぼんやりした相談を持ちかけられたときに、

何から手をつけたらいいのか迷わないための地図みたいなものです。

じゃあ一緒に見ていきましょう。

1.1 ビジネス課題とデータサイエンスの問題

この節ではビジネス課題と算数の文章題がどう違うのかを見ていきます。

ビジネス課題というのは、現場で出てくる「どうしたらいいですか?」をそのまま指します。

文章題というのは、小学校でやったあの「太郎くんが分速80メートルで…」のやつです。

この節が終わると、ビジネス課題を解くにはまず「文章題に書き直すところから始めないといけない」ということが腑に落ちます。

じゃあ始めましょう。

まずは情報が揃った文章題から

ウォーミングアップに、こういう問題を考えてみてください。

花子さんが分速80メートルで歩いて出かけてしまいました。

20分後に、太郎くんが追いかけることに気づきました。

太郎くんは50分以内に追いつきたいのですが、分速何メートルで走ればいいでしょうか?

ここまで条件が揃っていれば、あとは式を立てるだけです。

太郎くんが分速 x メートルで走るとして、追いつくのにかかる時間は

「花子さんがすでに進んだ距離」を「太郎くんと花子さんの速度差」で割ったものになります。

a = 80   # 花子さんの分速
b = 20   # 花子さんが先に出発した分数
# 50分以内に追いつくには (a*b) / (x - a) <= 50 が必要
# これを x について解くと
x_min = a * (b + 50) / 50
x_min  # → 112.0

分速112メートル以上で走れば追いつけます。

シンプルですね。

ビジネスの現場ではこうはいかない

ところが、現実のビジネス課題はこの文章題のようにはなっていません。

例えば店長から「うちの店、最近お客さんが減ってきたから何とかしてほしい」と相談されたとします。

このとき何が「速度」で、何が「時間」で、何が「距離」なのかは、誰も書いてくれていません。

そもそも何を求めれば「何とかした」ことになるのかすらはっきりしていません。

ここがビジネス課題と文章題の決定的な違いです。

文章題は問題が完成した状態で渡されますが、

ビジネス課題は問題を作るところから始めないといけません。

太郎くんの話に戻ってみる

さっきの太郎くんの問題を、もう一度ぼんやりさせてみます。

「花子さんに追いつきたい」

だけが伝えられている、と想像してください。

このとき僕たちがやることは、こういう順番です。

まず、何を達成したいのかをはっきりさせます。

「花子さんに50分以内に追いつくこと」をゴールに決めます。

次に、どうやってそれを達成するのか、手段を決めます。

「太郎くんが走って追いつく」という手段を選びます。

戻ってきてもらう、邪魔をする、みたいな手も理屈の上ではあり得ますが、今回は採用しません。

それから、解くために何を知る必要があるのかを書き出します。

花子さんがどのくらい前に出たのか、どのくらいの速度で動いているのか、この2つです。

最後に、データで埋まらない部分には仮定を置きます。

花子さんの速度が一定だ、という仮定を置きました。

途中で疲れて遅くなるかもしれないけど、今回はそこまで考えません。

ここまでやって、ようやく式が立てられる状態になります。

仮定の置き方には性格がある

仮定を強く置くと、少ない情報からでもズバッと答えが出せます。

その代わり、仮定が間違っていたら答えも丸ごと間違います。

逆に仮定を弱くすると、答えが安全ですが、はっきりした結論が出ないことが多くなります。

この「はっきり言うか、慎重にいくか」のさじ加減は、後の章でも何度も出てくる大事な感覚です。

今は「仮定にはトレードオフがある」とだけ覚えておけば十分です。

ここまでできるようになったこと

ビジネス課題と文章題のギャップが見えました。

文章題は条件が揃った状態で渡されるけど、

ビジネス課題は条件を自分で揃えるところから始まる、という話でした。

次は、その「条件を揃える」作業をどう型にしていくかを見ていきます。

1.2 ビジネス課題を解決するためのフレームワーク

この節ではこの本の主役になる3ステップフレームワークを紹介します。

3ステップというのは、この本がこれから5章ぶんかけて何度も繰り返し使う、共通の解き方の手順です。

どんなビジネス課題が来ても、毎回この3ステップに乗せて考えます。

この節が終わると、フレームワークの全体像がつかめて、

これ以降の章で「いま自分はどのステップにいるのか」が迷子にならずに追えるようになります。

じゃあ見ていきましょう。

3つのステップ

手順はこうです。

ステップ1:ビジネス課題を数理最適化問題として定式化する。

ステップ2:数理モデルを構築して、わからないパラメータをデータから推定する。

ステップ3:数理最適化問題を解いて、最適なアクションを導く。

それぞれ何をしているかを順番に見ていきます。

ステップ1:数理最適化問題として書き直す

ステップ1は、ぼんやりしたビジネス課題を「式」に書き直す作業です。

ここで使うのが数理最適化問題という形式です。

仰々しい名前ですが、やっていることは「ある条件のもとで、いちばんいい選び方を探す」ということだけです。

例えばお客さんが「予算100万円以内で、利益が最大になる広告の出し方を教えてほしい」と言ってきたとします。

予算100万円以内、というのが条件です。

利益が最大、というのが目的です。

広告の出し方、というのが自分で決められる部分です。

この3つを式にして並べたものが、数理最適化問題です。

ステップ2:足りないところをデータで埋める

ステップ1で式は書けたけど、現場ではそのままでは解けないことがほとんどです。

なぜかというと、式の中に「広告にいくら使ったら売上がいくらになるのか」みたいな、知らない関係性が入っているからです。

この知らない関係性を、データを使って推定するのがステップ2です。

過去の広告費と売上のデータがあれば、そこから「だいたいこういう関係になっている」と推定できます。

この推定の中身が、本書で何度も出てくる数理モデルです。

データが足りないときは、ドメイン知識から「こういう動きをするはずだ」という仮定を置いて補います。

太郎くんの話で言えば、花子さんの速度を「一般的な徒歩は分速80メートル」と仮定したのと同じです。

ステップ3:解いて、動く

ステップ3は、ステップ1で書いた式に、ステップ2で推定した数字を入れて、実際に解く作業です。

簡単な式なら手計算でいけますが、複雑なものは専用のアルゴリズムを使います。

解いた結果が、現場でやるべき具体的なアクションになります。

「分速112メートルで走れ」とか、「テレビCMに60万円、ネット広告に40万円配分しろ」とか、そういう形です。

このステップは行ったり来たりする

ここで大事なのは、3ステップは一方通行ではないということです。

ステップ2でいいデータが手に入らなかったら、ステップ1に戻って問題の立て方を変えます。

ステップ3で問題が複雑すぎて解けなかったら、ステップ1に戻って式をシンプルに直します。

最初から完璧な定式化を目指さず、行きつ戻りつしながら詰めていく感じです。

太郎くんの話を3ステップに乗せ直す

最後に、太郎くんの問題を3ステップで整理してみます。

ステップ1。

決められるのは太郎くんの速度 x です。

目的は、走るのは大変なので速度をできるだけ小さくすること。

条件は、50分以内に追いつくこと、そして花子さんより速く走ること。

これを式にすると、こうなります。

最小化したい : x
条件1       : (a * b) / (x - a) <= 50
条件2       : x > a

ステップ2。

a と b は知らない数なので、データや常識から推定します。

a は一般的な徒歩の分速ということで80、b は花子さんの普段の出発時刻から推測して20、と決めました。

ステップ3。

a=80、b=20 を入れて、条件を満たすいちばん小さい x を求めます。

a, b = 80, 20
x_min = a * (b + 50) / 50
x_min  # → 112.0

x = 112 が答えです。

太郎くんは分速112メートルで走れ、というのが今回の最適なアクションになります。

ここまでできるようになったこと

ビジネス課題を解くための共通の手順、3ステップフレームワークの全体像が見えました。

「式に書き直す → データで埋める → 解いて動く」の3段構えです。

次の節からは、この3ステップそれぞれを、もう少し丁寧に見ていきます。

1.3 ステップ1をもう少し丁寧に

この節ではステップ1の「数理最適化問題として定式化する」をじっくり見ます。

数理最適化問題には登場人物が3つあります。

目的関数、制約条件、決定変数(と定数)です。

この節が終わると、目の前のビジネス課題を見たときに

「これは何を最大化したい話で、何が変えられて、何が縛りなのか」

を切り分けられるようになります。

じゃあ始めましょう。

目的関数 ── 何を最大化(最小化)したいか

目的関数というのは、最終的にいちばん良くしたい指標のことです。

利益、売上、お客さん一人あたりの購入数、認知率、サイトへの訪問数。

逆に最小化したい例だと、費用、待ち時間、不良品の数、なんかが入ります。

「結局、何が良くなったら成功と言えるんですか?」をはっきりさせる作業です。

制約条件 ── 守らなきゃいけない縛り

制約条件は、最適化を考えるうえで必ず守らないといけない条件です。

予算、時間、品質基準、人員数、法律。

「予算は無限にあります」「時間も無限にあります」だと、ただの夢物語になってしまうので、

現実の縛りをここで書いておきます。

決定変数と定数 ── 動かせるものと動かせないもの

決定変数は、自分で値を変えられる要素です。

定数は、状況として決まっていて自分では変えられない要素です。

例えば「製品の製造数を決めたい」というとき、製造数は決定変数で、

製品1個あたりの売値は今回の判断では変えないので、定数です。

ここの線引きは状況によって変わります。

別の場面では、価格そのものを決定変数にすることもあります。

「今回の意思決定で何が動かせるんだっけ?」を毎回考えます。

数式の形

この3つを並べて書くと、数理最適化問題はこういう形になります。

最大化したい : f(x)
ただし     : x は X の範囲内

f(x) が目的関数で、x が決定変数で、X が「x が満たすべき条件」を集めたものです。

具体的な数字でやってみると、こんな感じです。

# 目的関数 f(x) = 2x を最大化したい
# 条件は 0 <= x <= 2
# このとき答えは x = 2、目的関数の値は 4
x_star = 2
f = 2 * x_star
f  # → 4

「条件の範囲で目的関数がいちばん大きくなる x を選ぶ」というだけのことです。

例1:ABテストを式にする

もう少し現場っぽい例を見ていきます。

ウェブサイトに出すバナー広告で、

デザインAとデザインBのどちらを使うか決めたい、

という課題があったとします。

決定変数は「AかBのどっちか」なので、x の取りうる値は {A, B} の2つだけです。

目的関数は、ステークホルダーと相談した結果、クリック率にしました。

クリック率を c(x) と書くと、c(A) は A のクリック率、c(B) は B のクリック率です。

制約条件は今回特になし、とします。

これを並べると、こうなります。

最大化したい : c(x)
ただし     : x は {A, B} のどちらか

c(A) と c(B) のうち大きいほうを選ぶ、というだけの話を、わざわざ式に書いただけです。

ただ、こうやって式にしておくと、

「クリック率が同じだったらどうする?」

「コストも考えるべきでは?」

みたいな次の論点が見つけやすくなります。

ここがステップ1の地味だけど大事なところです。

例2:営業チームの最適人員数

次は、もう少しビジネスっぽい例です。

うちの会社の営業チーム、いま何人いるのが正解なんだろう?

という課題を考えます。

決定変数は何でしょうか。

採用は今からできるけど、解雇はできないとします。

すると動かせるのは「これから何人増やすか」で、新しい人員数 x は今の人数より大きい範囲で選べます。

つまり x ≧ 今の人数、です。

目的関数は利潤にします。

人を増やせば売上は伸びます。

でも、増やしただけ人件費もかかります。

利潤は売上から人件費を引いたものなので、人を増やしたら必ず増えるとは限りません。

人員 x に対して利潤がどう変わるかを表す関数を P(x) と書いて、これを最大化します。

人員数  1   2   3   4   5   6   7
利潤   30  50  60  65  60  50  35

例えばこんな感じで、ある人数を超えると利潤がむしろ下がっていきます。

売上の伸びより人件費の伸びが大きくなる、という現象です。

制約条件は、採用にかけられる予算 b と、一人あたりの人件費 c です。

「採用にかかる総額 c × x が予算 b 以下」という縛りが入ります。

これを並べると、こうなります。

最大化したい : P(x)
条件1       : c * x <= b   (予算の上限)
条件2       : x >= 今の人数  (解雇しない)

「利潤を最大にする人員数 x を予算内で選んでね」という問題に翻訳できました。

例3:広告出稿の最適水準

もう一つ、広告まわりの例も見ておきます。

うちの製品、テレビCMをどのくらい打てばいいんだろう?

という課題です。

決定変数は広告の出稿量 x です。

マイナスの出稿はできないので x ≧ 0、というだけの縛りです。

目的関数は製品の認知率にしましょう。

CMをたくさん打てば、たくさんの人が製品を知ります。

ただ、お金を無限に使えるわけじゃありません。

広告にかけられる予算 b、出稿1単位あたりの費用 c があるので、

「総出稿費 c × x が予算 b 以下」が制約条件です。

これを並べるとこうなります。

最大化したい : a(x)        (認知率)
条件1       : c * x <= b  (予算の上限)
条件2       : x >= 0

ABテストも、営業チームも、広告出稿も、

形は違うけど「目的関数・制約条件・決定変数」の3点セットで書ける、というのが伝わったでしょうか。

これがステップ1の威力です。

なぜわざわざ数理最適化問題にするのか

ところで、なんでこんな式に書き直す手間をかけるんでしょうか。

ぱっと思いつく行動でとっとと動いたほうが速くないか、と思うかもしれません。

理由は3つあります。

ひとつめ。

目的関数を決める作業を通して、「何が良くなったら成功と言えるか」がはっきりします。

売上を伸ばすのか、利潤を増やすのか、離脱率を下げるのか、新規獲得を優先するのか。

ここを最初に明文化しておかないと、分析の終盤で「結局なんの話してたんだっけ?」になります。

ふたつめ。

決定変数を決める作業で、「どの手段を検討の対象にするか」がはっきりします。

広告出稿量を増やすのか、クリエイティブを変えるのか、YouTubeに配信先を広げるのか。

ここを絞らないと、無限の選択肢を漠然と考えることになって、議論が進みません。

みっつめ。

制約条件を決める作業で、現実の縛りが見える化されます。

予算、人員、組織内の力関係、慣習、暗黙の前提。

これを書き出すと、「実はこの暗黙の前提、見直したほうがよくない?」という気付きにもつながります。

データ分析というのは、それ単体では価値を生みません。

分析結果を受けて誰かが意思決定して、何かが動いて、初めて価値が出ます。

数理最適化問題に書き直す作業は、

「分析の結果を受けて、具体的にどう動くのか」

を最初に決めておく作業でもあるんです。

成果は確率的に動く ── 条件付き期待値の登場

ここまでで定式化はできました。

でも、この段階ではまだ最適なアクションは決められません。

なぜかというと、x(アクション)と成果の関係が、まだわからないからです。

ABテストでは c(A) と c(B) が、営業チームでは P(x) が、広告では a(x) が、

それぞれ未知のままです。

この関係を埋めるのがステップ2の仕事ですが、その前に一つ、しれっと大事な話を挟んでおきます。

それは「同じアクションをとっても、成果は毎回ぴったり同じにはならない」ということです。

例えばABテストでクリエイティブAを表示しても、必ずクリックされるわけではないし、

絶対クリックされない、というわけでもありません。

クリックする日もあれば、しない日もあります。

人によって興味も違うし、その日の気分や時間の余裕でも変わります。

成果は、いってみれば「サイコロを振っているような感じ」で出てきます。

このとき、「アクションAとアクションBのどっちが良いか」をどうやって判断すればいいでしょうか。

一発勝負で決めるのは無理筋です。

普通は、平均的にどのくらいの成果が出るかで比べます。

実際、ABテストでも「Aのクリック率」「Bのクリック率」を比較しますよね。

これがまさに平均(期待値)を見ているということです。

数学の言葉だと、これを条件付き期待値と呼びます。

「アクション x を選んだとき、成果 y はだいたいいくつになるか」を表す数字、と思ってください。

記号では E[y | x] と書きます。

この記号が後の章でもうるさいくらい出てくるので、慣れておきましょう。

「| x」の部分は「x という条件のもとで」という意味です。

3つの例を条件付き期待値で書き直す

さっきの3つの例を、この条件付き期待値で書き直しておきます。

ABテスト。

クリックされたかどうかを 0 か 1 で表す変数 c とすると、

クリック率は「c が 1 になる確率」つまり Pr(c=1 | x) で、これは E[c | x] と同じです。

なので問題はこうなります。

最大化したい : E[c | x]   (クリック率)
ただし     : x は {A, B} のどちらか

営業チーム。

利潤を確率変数 P で表すと、人員 x に対する平均利潤は E[P | x] です。

最大化したい : E[P | x]
条件1       : c * x <= b
条件2       : x >= 今の人数

広告出稿。

認知されたかを 0 か 1 で表す変数 A とすると、認知率は Pr(A=1 | x) = E[A | x] です。

最大化したい : E[A | x]   (認知率)
条件1       : c * x <= b
条件2       : x >= 0

形が見事に揃いました。

3つとも「条件付き期待値を最大化する x を、制約のもとで探す」という同じ形です。

ステップ2でやるのは、まさにこの E[y | x] の中身をデータから推定することになります。

ちなみに、本当に期待値だけでいいのか

ここで一応断っておくと、平均値(期待値)だけ見るのが絶対正解というわけではありません。

例えば「平均は高いけど、ハズれたときの落ち込みが激しい」打ち手と、

「平均は普通だけど、ブレが小さい」打ち手があったら、

人によって・状況によって、後者を選ぶこともあります。

そういう場合は、ばらつき(分散)も合わせて考えたり、

下振れの度合い(10%点とか)を指標にしたりします。

ただ、最初の一歩としては期待値が扱いやすいので、本書ではこちらで進めます。

ここまでできるようになったこと

数理最適化問題の3つの登場人物が見えました。

目的関数、制約条件、決定変数と定数、です。

ABテスト、営業チームの人員、広告出稿、

業種が違っても全部この型に乗せられる、という感覚もつかめたと思います。

そして、成果がサイコロ的にブレる現実に対しては、

条件付き期待値 E[y | x] という道具で「平均的な成果」を扱う、という流れも押さえました。

次は、この E[y | x] の中身をどうやってデータから推定するか、というステップ2の話です。

1.4 ステップ2をもう少し丁寧に

この節ではステップ2の「数理モデルを構築してデータから推定する」をじっくり見ます。

ステップ1で問題は式に書けたけど、

E[y | x] の中身が空欄のままじゃ解けない、という状態でした。

ここを埋めにいきます。

埋め方には大きく2つあります。

ひとつは、データをそのまま集計して平均を出す方法(単純集計)。

もうひとつは、データに合う関数の形を仮定して当てはめる方法(数理モデル)。

この節が終わると、

「単純集計でいけるのか、数理モデルが必要なのか」

「数理モデルを使うなら、どうやって関数の形を決めるのか」

の判断ができるようになります。

じゃあ始めましょう。

単純集計:いちばんシンプルな方法

まず、いちばん素直な方法は、アクションごとに成果の平均を出すことです。

これを単純集計と呼びます。

ABテストはこれで足ります。

クリエイティブAをランダムに n 人に表示して、何人クリックしたかを数えます。

# Aを表示した人数とクリック数
n_a = 1000
click_a = 100
# Bも同様
n_b = 1000
click_b = 200

# 単純集計でクリック率(条件付き期待値)を推定
ctr_a = click_a / n_a
ctr_b = click_b / n_b
ctr_a, ctr_b  # → (0.1, 0.2)

E[c | x=A] が0.1で、E[c | x=B] が0.2、と推定できました。

データを足して割っただけです。

「なんだ、これでいいなら数理モデルなんていらないじゃないか」と思うかもしれません。

ABテストみたいに、

選択肢が少なくて、各選択肢にちゃんとデータがある状況なら、本当にこれで足ります。

でも、現実はそうはいかない

問題は、現実のビジネス課題が必ずしもABテストみたいな形をしていないことです。

例えば、こういう状況だと単純集計は使えません。

選択肢が多すぎる、または連続的な場合。

広告予算をいくらにするか決めるとき、x = 100万、200万、300万…と無限の候補があります。

過去に300万を試した実績がない場合、その金額の成果は集計しようがありません。

アクションと成果の関係が間接的な場合。

営業チームの人員を変えたら、まず売上が動いて、人件費も動いて、最後に利潤が決まります。

このとき、利潤を直接見るより、「人員と売上」「人員と人件費」を別々に追ったほうが筋がいいです。

こういう状況で出てくるのが、数理モデルです。

数理モデルってなにを仮定しているのか

数理モデルの正体は、「アクション x と成果 y の間にこういう関係があるはずだ」という関数の形を仮定して書き下すことです。

いちばんシンプルな例は線形回帰モデルです。

E[y | x] = α + β * x

x が増えると、y も比例して増える、という関係を仮定しています。

α は切片(x=0 のときの y)で、β は傾き(x が1増えたら y がいくつ増えるか)です。

未知のパラメータが2つしかないので、

理屈の上ではデータが2点あればもう推定できてしまいます。

データが少なくても何とか答えが出せるのが、関数形を仮定することの一番のメリットです。

その代わり、仮定が現実とズレていると、答えも盛大にズレます。

ここのバランスをどう取るかが、ステップ2の腕の見せどころです。

例:身長から体重を予測する

ここから、ドメイン知識を数理モデルに反映させる、という話の決定版を見ていきます。

身長から体重を予測する、という問題で考えます。

実は本に出てくる例なんですが、これがとてもよくできていて、

「ドメイン知識でモデルを作るとは何か」が一発で腑に落ちます。

データの紹介

国民健康・栄養調査というデータから、

性別と年齢層ごとの平均身長と平均体重のデータが手に入るとします。

身長が低いところは子ども、高いところは大人、という感じで分布しています。

# イメージ:身長と体重の散布図
# 100cm → 約16kg
# 120cm → 約24kg
# 140cm → 約36kg
# 160cm → 約55kg
# 180cm → 約75kg

ここで、ちょっといじわるな問題設定にします。

手元にあるのは身長100cm〜140cmの人のデータだけ、ということにします。

このデータを使って、

100cm未満の人の体重も、140cm超の人の体重も予測したい、と。

データの外側を予測することを外挿(がいそう)と言います。

これがけっこう難しいんです。

線形モデルでやってみる

まずは素直に線形回帰でいきます。

E[体重 | 身長] = α + β * 身長

100cm〜140cmのデータに直線を引きます。

直線が引けたら、その直線を150cmや90cmまで延長して予測します。

import numpy as np

# 学習データ(100〜140cm)
heights = np.array([100, 110, 120, 130, 140])
weights = np.array([16, 20, 24, 30, 36])

# 直線フィット
beta, alpha = np.polyfit(heights, weights, 1)

# 学習範囲外を予測
def predict_linear(h):
    return alpha + beta * h

predict_linear(180)  # → 直線を延長した値

結果はどうかというと、学習データの範囲内ですらカーブを捉えきれずにズレます。

身長と体重の関係は、まっすぐな直線じゃなくて、ちょっと反り上がるカーブを描いているからです。

「線形だ」という仮定が、現実とは合っていなかったわけです。

じゃあもっと柔軟にしてみる

直線だとダメなら、もっとぐにゃぐにゃ曲げられるモデルにしてみます。

3次まで含めるとこうなります。

E[体重 | 身長] = β0 + β1*身長 + β2*身長² + β3*身長³

パラメータが4つに増えたぶん、いろんな形に曲がります。

# 3次フィット
coeffs = np.polyfit(heights, weights, 3)

def predict_cubic(h):
    return np.polyval(coeffs, h)

100cm〜140cmの範囲ではバッチリ合います。

ところが、範囲の外、特に180cmあたりを予測してみると、

「あれ、急に体重が減っていく?」とか、

「90cmの人がやたら重いことになってる?」とか、

直感とは正反対の予測になってしまうことがあります。

3次関数の形がそうさせるんです。

データに合うように曲げた結果、データのない領域では暴走している、という状態です。

これは複雑なモデルが外挿で起こしがちな失敗です。

ここでドメイン知識を投入する

線形だと固すぎ、3次だと暴れすぎ。

じゃあどうするか、というところで使うのがドメイン知識です。

ドメイン知識というのは、その問題の対象(今回は人体)に対して、

「現実ってこういう仕組みになってるはず」という知識のことです。

人間の体重は、何で決まるかを物理的に考えてみます。

まず、人間の体は、密度がだいたい一定だと考えていいでしょう。

ということは、体重は体積に比例します。

体重 ≈ γ × 体積

γ は密度を表す定数です。

次に、体積は「縦 × 横 × 高さ」で計算できます。

高さは身長そのものです。

縦幅と横幅は、まあ大雑把に言えば身長に比例して大きくなる、と仮定できます。

縦幅 = α × 身長、横幅 = β × 身長、と置きます。

すると体積はこうなります。

体積 = 高さ × 縦幅 × 横幅
     = 身長 × (α × 身長) × (β × 身長)
     = α × β × 身長³

体積は身長の3乗に比例する、という関係になります。

これと体重 ≈ γ × 体積 を組み合わせると、

体重 ≈ γ × α × β × 身長³

γ × α × β はただの定数なので、まとめて θ と書くと、最終的なモデルはこうなります。

E[体重 | 身長] = θ × 身長³

未知のパラメータが、たった一つ θ だけになりました。

これを使うとどうなるか

heights = np.array([100, 110, 120, 130, 140])
weights = np.array([16, 20, 24, 30, 36])

# 体重 = θ * 身長^3 の θ を推定
theta = np.sum(weights * heights**3) / np.sum(heights**6)

def predict_cube(h):
    return theta * h**3

predict_cube(180)  # → だいたい体重70kg台の予測
predict_cube(90)   # → だいたい体重12kg前後の予測

学習データの範囲外でも、人間として違和感のない値が出てきます。

3次関数をデータに当てはめたときと同じ「3乗」を使っているのに、

データのない領域での挙動がぜんぜん違います。

なぜかというと、3次関数は4つのパラメータがデータに合わせて自由に動いていて、

データのない領域は誰もコントロールしていなかったからです。

一方、ドメイン知識から導いたモデルは、

「3乗に比例する」という形をガチッと固定したうえで、係数 θ だけをデータで決めています。

形が物理的な根拠で決まっているので、データのない領域でも筋が通ります。

これがドメイン知識を数理モデルに反映させるということの正体です。

データを補ってくれる「事前情報」を、関数の形そのものに埋め込んでいる、と言ってもいいです。

モデル構築の心得

この例から学べることをまとめておきます。

データの量が少ないときは、シンプルなモデル+強い仮定で押し切る。

データが豊富なら、複雑なモデルでデータに語らせる。

そして、現象の裏側にどういうメカニズムがあるかを、頭に汗をかいて考える。

「なぜそうなっているのか」を物理や生物や経済の言葉で説明できると、

それがそのまま関数の形になります。

身長体重の話は1章だけで終わりますが、

このあとの章でテレビCMのモデリングをするときも、

まったく同じ考え方を使います。

「広告に何回触れたら認知が上がるか」を、

「人がCMを目にする回数の分布はどうなっているか」というドメイン知識から導きます。

ここの感覚はとても大事なので、ちょっと長めに紹介しました。

例:営業チームを再考する

身長体重の話を踏まえて、さっきの営業チームの問題に戻ります。

利潤の条件付き期待値 E[P | x] を、データから直接推定するのは大変です。

人員を1人ずつ増やして利潤の変化を見る、なんて実験はビジネスじゃできません。

過去のデータから引っ張ってこようにも、過去にやったことのない人員数の利潤はわかりません。

そこで、ドメイン知識で問題を分解します。

利潤って何でしたっけ。

「売上 − 費用」ですよね。

これは定義なので、間違いようがありません。

つまり、

E[利潤 | 人員数] = E[売上 | 人員数] - E[費用 | 人員数]

と書けます。

さらに、一人あたりの人件費 c が固定だとすれば、人員 x のときの費用は c × x です。

すると、

E[利潤 | 人員数] = E[売上 | 人員数] - c × x

になります。

問題が「利潤を直接予測する」から「売上を予測する」に変わりました。

費用の部分は完全に切り離せたので、残ったのは売上だけです。

問題が一段、簡単になりました。

これがドメイン知識による問題の分解です。

KPIツリーという図で表現することもあります。

利潤
├─ 売上 = 一人あたりの売上 × 人員数
└─ 費用 = 一人あたりの人件費 × 人員数

ツリーで描くと、どこに数理モデルを当てるべきか、どこは定義のまま使えるかがはっきり見えます。

売上の数理モデル

残った売上 E[売上 | 人員数] をどうモデリングするか。

いちばんシンプルなのは、「一人あたりの平均売上 r は人員数によらず一定」という仮定です。

E[売上 | 人員数 x] = r × x

過去の人員数と売上のデータから r を推定すればOKです。

ただ、この仮定は現実とズレてそうです。

人を増やしすぎると、ひとり当たりの売上はちょっとずつ下がっていく、というのはありそうな話です。

そういうときは、ちょっと仮定を緩めて、

E[売上 | 人員数 x] = α × x^β   (0 < β < 1)

という形を使います。

β が1より小さいので、x が増えても売上の伸びは徐々に頭打ちになります。

身長体重の3乗則と同じく、関数の形にドメイン知識(収穫逓減の法則)が埋め込まれています。

パラメータが (α, β) の2つなので、過去のデータが2点あれば推定できます。

最終的に、利潤の条件付き期待値はこうなります。

E[利潤 | 人員数 x] = α × x^β - c × x

利潤を直接モデリングするより、ずっと素直に組み立てられました。

例:広告出稿を再考する

最後に、広告出稿の例も同じやり方で組み立てておきます。

「広告を打つほど認知率は上がるけど、増え方は徐々に小さくなる」というドメイン知識を持っているとします。

これを表す関数の形として、対数を使うやり方があります。

Pr(認知 = 1 | 出稿量 x) = 1 - exp(-λ × x)

x が増えるほど認知率は1に近づきますが、頭打ちになっていきます。

λ がどのくらいの速さで認知が積み上がるかを表すパラメータです。

実は2章以降では、もっと丁寧に、

「CMにどのくらいの人が何回触れるか」と「触れた回数ごとに何%が認知するか」に分けてモデリングしていきます。

身長体重の話と同じで、

ドメイン知識を関数の形に翻訳すると、データが少なくても筋のいい予測ができるようになります。

ここまでできるようになったこと

ステップ2の中身が見えました。

データが揃っていれば単純集計、足りなければ数理モデル、という使い分けです。

数理モデルを使うときは、データに合わせて関数を曲げるんじゃなくて、

「現象の裏側でどういう仕組みが働いているか」を考えて、関数の形を決める。

これが、データの少ない領域でも信頼できる予測をするためのコツでした。

次はいよいよ、ここまで組み立てた問題を解いていきます。

1.5 ステップ3:解いて最適なアクションを導く

この節ではステップ3で実際に問題を解いていきます。

ステップ1で式を書いて、ステップ2でデータと数理モデルで E[y | x] を推定したので、

あとは制約条件のなかで目的関数が最大になる x を選ぶだけです。

シンプルな問題は手計算でいけるし、複雑な問題は専用のアルゴリズムを使います。

3つの例で具体的にやってみます。

ABテストを解く

クリック率の推定値が出ています。

E[c | x=A] = 0.1
E[c | x=B] = 0.2

選択肢は2つだけです。

大きいほうを選んで、最適なアクションは「Bを採用」です。

おわり。

数理最適化と言ってもピンキリで、選択肢が少ないときはこれだけです。

営業チーム最適人員数を解く(線形仮定の場合)

まずは「一人あたり売上 r は一定」という線形の仮定から。

問題はこうでした。

最大化したい : r * x - c * x = (r - c) * x
条件1       : c * x <= b   (予算上限)
条件2       : x >= 今の人数 (解雇しない)

目的関数は (r - c) × x なので、r と c のどっちが大きいかで結論が割れます。

def best_headcount_linear(r, c, b, current):
    if r > c:
        # 1人増やすたび利潤が増えるので、予算上限まで採用
        return int(b // c)
    elif r < c:
        # 1人増やすたび利潤が減るので、採用しない
        return current
    else:
        # 利潤が変わらないので、好きにしてよい
        return current

線形の仮定だと、答えは「全力採用」か「採用ゼロ」の二択になります。

中庸の答えが出てこないのは、ちょっと現実離れしています。

ここはモデルの仮定が雑すぎたということですね。

営業チーム最適人員数を解く(収穫逓減モデルの場合)

そこで、もう一個の数理モデル E[売上 | x] = α × x^β を使った版を解きます。

最大化したい : α * x^β - c * x
条件1       : c * x <= b
条件2       : x >= 今の人数

x で微分して0と置くと、最適な x が解析的に出ます。

人を1人増やしたときに増える売上が、増える人件費とちょうど釣り合うところが最適、という感覚です。

d/dx (α * x^β - c * x) = α * β * x^(β-1) - c = 0

これを x について解くと、

x* = (α * β / c)^(1/(1-β))

になります。

def best_headcount(alpha, beta, c, b, current):
    x_star = (alpha * beta / c) ** (1 / (1 - beta))
    # 制約に照らして調整
    upper = b / c
    if x_star < current:
        return current   # 下限に張り付き
    if x_star > upper:
        return int(upper)  # 予算上限に張り付き
    return x_star

best_headcount(alpha=10, beta=0.5, c=2, b=100, current=5)
# → モデルから出た最適人員数(端数は実装上どう扱うかで変わる)

線形のときと違って、

「現状人員」と「予算上限」の間のどこかに、ちゃんとした最適解が落ちます。

これがドメイン知識をモデルに入れたごほうびです。

広告出稿の解は2章へ

最後の広告出稿の問題は、認知率のモデルがもう少し丁寧な形になります。

そして、これは1章のサンプル問題ではなく、本書の本丸です。

なので、解くのは2章以降に持ち越しです。

ここではステップ3の手触りだけ持って終わります。

「式に書いて、データを入れて、解く」。

シンプルな問題なら手計算、複雑なら数値計算。

このあとの章では、scipy なんかを使って数値的に解いていきます。

ここまでできるようになったこと

3ステップを最後まで通せました。

定式化 → 数理モデルで条件付き期待値を埋める → 解いて x* を出す。

問題の複雑さによって、ステップ3の難易度は大きく変わるけど、

「式と数字が揃っていれば、あとはアルゴリズムが解いてくれる」という安心感が得られます。

1.6 この章のまとめ

この章では、ビジネス課題を解くための共通の手順として3ステップフレームワークを覚えました。

ステップ1でビジネス課題を「目的関数・制約条件・決定変数」の3点で式に書き直して、

成果が確率的に動く現実に合わせて条件付き期待値 E[y | x] で目的関数を表現する。

ステップ2で、データが揃っていれば単純集計、足りなければ数理モデルを使って E[y | x] を推定する。

数理モデルでは、データを丸暗記させるのではなく、

ドメイン知識から導かれる関数の形を仕込むことで、データの少ない領域でも筋のいい予測をする。

身長と体重の3乗則、営業チームのKPIツリー分解、広告出稿の対数モデル、

すべて同じ「ドメイン知識を関数の形に埋め込む」という発想でした。

ステップ3で、定式化した問題を解いて、現場で実行できる具体的なアクションに落とす。

シンプルな問題は解析的に、複雑な問題は数値計算で。

この型は、テレビCMの話だけじゃなく、業界を問わず使える考え方として用意されています。

2章からは、実際のテレビCMデータを使って、この3ステップを本気で回していきます。

「テレビCMに何回触れたら、どのくらいの人が商品を覚えてくれるのか」という関係を、

ポアソン分布や負の二項分布といった確率分布で数理モデルにしていきます。

身長体重の3乗則と同じで、

CMの世界にもCMの世界のドメイン知識があるんだな、と思いながら読んでいくと楽しいはずです。

僕から以上!あったかくしてねろよー