機械学習ではなぜlogを取るのか:五つの理由を整理する
こんにちは。ゆうせいです。
機械学習を学んでいると、あちこちで対数が登場します。交差エントロピー誤差の式にlogがあります。尤度を最大化するとき、対数尤度を使います。前処理でデータを対数変換することもあります。損失のグラフを描くとき、縦軸を対数目盛りにすることもあります。これだけ頻繁に出てくると、「なぜlogばかり出てくるのか」と感じるのではないでしょうか。実は、それぞれ理由が異なります。同じ対数という道具を、まったく別の目的で使っているのです。本記事では、機械学習で対数を取る理由を五つに整理し、それぞれの場面と目的を解説します。
結論を先に述べます
機械学習で対数を取る理由は、大きく五つに分類できます。
第一に、掛け算を足し算に変えるためです。確率の積を扱う場面で使われます。
第二に、数値計算を安定させるためです。非常に小さい値や大きい値による破綻を防ぎます。
第三に、値の範囲を制限から解放するためです。0から1の確率を、実数全体に広げます。
第四に、偏った分布を整えるためです。前処理としてのデータ変換にあたります。
第五に、桁の違うものを比べやすくするためです。グラフの描画や、パラメータの探索範囲の設定で使われます。
以下、順に説明します。
対数の基本を確認する
本題に入る前に、対数の性質を確認しておきます。
対数とは、「何乗したらその数になるか」を表すものです。
これは、2を3乗すると8になる、という意味です。
機械学習では、底がネイピア数eの自然対数がよく使われます。
表記としては、単にlogと書かれることも多くあります。文脈から、自然対数だと判断してください。
重要な三つの性質
以降の説明で使う性質を、先に挙げておきます。
第一の性質は、積が和になることです。
第二の性質は、単調増加であることです。
xが大きくなれば、 も必ず大きくなります。順序が保たれます。
第三の性質は、値の範囲を広げることです。
xが0に近づくと、 はマイナス無限大に近づきます。0から1という狭い範囲が、マイナス無限大から0という広い範囲に引き伸ばされます。
理由1:掛け算を足し算に変える
最も本質的な理由です。
確率は掛け算で積み重なる
複数の独立な事象が同時に起こる確率は、それぞれの確率の掛け算になります。
コインを3回投げて、すべて表が出る確率は、次のようになります。
機械学習では、この構造が頻繁に現れます。
たとえば、1000件のデータがあるとき、そのすべてが観測される確率は、1000個の確率の掛け算になります。
これを尤度と呼びます。モデルのパラメータが、観測されたデータをどれだけうまく説明できるかを表す指標です。
掛け算のままでは扱いにくい
この掛け算には、二つの問題があります。
一つ目は、微分が面倒であることです。
積の微分には、積の法則を使う必要があります。項が1000個あれば、非常に複雑な式になります。
二つ目は、数値が極端に小さくなることです。この点は、理由2で詳しく扱います。
対数を取ると足し算になる
対数の第一の性質を使います。
掛け算が、足し算に変わりました。
足し算であれば、微分は各項を独立に微分して足すだけです。
計算が、劇的に簡単になります。
最大値の位置は変わらない
ここで、重要な確認があります。
対数を取ったら、最大化する対象が変わってしまうのではないか、という疑問です。
答えは、変わりません。
対数は単調増加関数です。したがって、 が最大になるパラメータと、
が最大になるパラメータは、完全に一致します。
「最大値そのもの」は変わりますが、「最大値を取るパラメータ」は同じです。求めたいのは後者ですから、問題ありません。
この性質があるからこそ、安心して対数を取れます。

理由2:数値計算を安定させる
アンダーフローという問題
コンピュータが扱える数値には、限界があります。
0.001という確率を、1000個掛け合わせてみます。
この値は、float64という一般的な形式で表現できる下限を、はるかに下回ります。
結果として、コンピュータ上では0になってしまいます。これをアンダーフローと呼びます。
0になってしまえば、その後の計算はすべて意味を失います。対数を取ろうにも、 はマイナス無限大であり、計算が破綻します。
対数を取れば桁が扱いやすくなる
同じ計算を、対数の世界で行います。
マイナス6900という値になります。
これは、float64で問題なく表現できます。
極端に小さい数値も、対数の世界では、扱いやすい大きさの数値になります。
オーバーフローへの対策
逆向きの問題もあります。
Softmax関数の計算では、指数関数が使われます。
が大きいと、
が表現可能な上限を超えます。これをオーバーフローと呼びます。
この値は計算できません。
対策として、すべての要素から最大値を引いてから計算する、という工夫が行われます。
分子と分母の両方が同じ定数倍されるため、結果は変わりません。しかし、指数関数の入力が必ず0以下になるため、オーバーフローを避けられます。
LogSumExpという技法
対数と指数を組み合わせた計算では、次の形がよく現れます。
これを、次のように変形します。
この変形により、数値的に安定した計算ができます。
この技法を、LogSumExpと呼びます。多くのライブラリに、専用の関数が用意されています。
実装上の注意
PyTorchなどのフレームワークでは、Softmaxと対数の計算をまとめた関数が提供されています。
分けて計算するより、まとめたほうが数値的に安定します。
import torch
import torch.nn.functional as F
logits = torch.tensor([100.0, 101.0, 102.0])
# 推奨されない方法:分けて計算
probs = F.softmax(logits, dim=0)
log_probs_unstable = torch.log(probs)
# 推奨される方法:まとめて計算
log_probs_stable = F.log_softmax(logits, dim=0)
分類問題で使うnn.CrossEntropyLossも、内部でこの工夫を行っています。そのため、Softmaxを適用する前の値、つまりロジットを直接入力する設計になっています。

理由3:値の範囲を制限から解放する
確率は0から1に閉じ込められている
確率は、必ず0から1の範囲に収まります。
この制約が、都合の悪い場面があります。
ロジスティック回帰を考えます。入力変数の線形結合から、確率を予測したいとします。
この は、マイナス無限大からプラス無限大まで、どんな値にもなり得ます。
これを、そのまま確率として扱うことはできません。範囲が合わないためです。
ロジット変換
そこで、確率を実数全体に写す変換を使います。
まず、オッズを計算します。事象が起こる確率と、起こらない確率の比です。
pが0に近ければ0に近く、pが1に近ければ無限大に近づきます。
次に、この対数を取ります。
これをロジット変換と呼びます。
pが0に近づけばマイナス無限大に、pが1に近づけばプラス無限大に、pが0.5であれば0になります。
0から1という制限された範囲が、実数全体に広がりました。
線形結合と結びつく
これにより、次の関係を仮定できます。
両辺とも、実数全体の値を取れるため、無理がありません。
この式をpについて解くと、シグモイド関数が導かれます。
ロジスティック回帰という名前は、この「ロジットを線形回帰する」という発想に由来しています。
解釈しやすくなるという副次的な利点
ロジット変換には、もう一つ利点があります。
ロジスティック回帰の係数が、オッズ比として解釈できるようになるのです。
ある変数の係数が0.7であれば、次のようになります。
その変数が1単位増えると、オッズが約2倍になる、と説明できます。
医療統計や与信審査など、モデルの判断根拠を人に説明する必要がある場面で、この性質が重視されます。

理由4:偏った分布を整える
ここまでは、モデルの内部計算の話でした。理由4は、前処理の話です。
実データは右に裾を引くことが多い
現実のデータには、極端に偏った分布を持つものが少なくありません。
年収、企業の売上、都市の人口、Webサイトの訪問数、単語の出現頻度。
これらに共通するのは、大多数が小さな値に集中し、ごく一部が極端に大きな値を取るという構造です。
年収であれば、大部分の人が300万円から800万円の範囲に収まる一方、数億円という人も存在します。
このような分布を、右に裾を引く分布と呼びます。
何が問題なのか
三つの問題があります。
第一に、外れ値の影響が大きすぎることです。
数億円という値が1件あるだけで、平均が大きく引き上げられます。多くのモデルは、誤差の2乗を最小化するため、極端な値に強く引きずられます。
第二に、線形の関係が見えにくいことです。
「年収が2倍になると、購買額はどう変わるか」という関係は、多くの場合、掛け算の関係です。しかし、線形モデルは足し算の関係しか表現できません。
第三に、スケールの差が大きすぎることです。
第2章で扱った標準化を行っても、分布の形が偏っていれば、問題は完全には解消されません。
対数変換で分布が整う
対数を取ると、大きな値ほど強く圧縮されます。
10倍になっても、対数の値は約2.3ずつしか増えません。
この性質により、右に裾を引いた分布が、より対称的な形に近づきます。
掛け算の関係を足し算に変える
もう一つの効果があります。
このような関係があるとします。掛け算とべき乗の関係です。線形モデルでは表現できません。
両辺の対数を取ります。
と
の間に、線形の関係が現れました。
つまり、対数変換した後であれば、線形モデルで扱えるようになります。
実装上の注意
対数変換には、注意点があります。
0以下の値には、対数が定義できません。
売上や訪問数のように、0が含まれるデータでは、そのまま対数を取れません。
対策として、1を足してから対数を取る方法がよく使われます。
xが0のとき、 となり、問題が生じません。
NumPyには、この計算を数値的に安定して行う関数が用意されています。
import numpy as np
x = np.array([0, 1, 10, 100, 10000])
print(np.log1p(x))
なお、対数変換したデータで予測を行った場合、結果を元のスケールに戻す必要があります。この際、単純に指数関数を適用すると、平均のずれが生じる点にも注意が必要です。

理由5:桁の違うものを比べやすくする
最後は、可視化と探索の話です。
対数目盛りのグラフ
学習の経過を示すグラフで、縦軸を対数目盛りにすることがあります。
損失が、最初は100で、最終的に0.001まで下がったとします。
通常の目盛りでは、100という初期値に引きずられて、0.001付近の変化がまったく見えません。グラフの下端に張り付いた直線に見えます。
対数目盛りにすれば、100から10への変化と、0.01から0.001への変化が、同じ幅で表示されます。
どちらも10分の1になるという、同じ意味を持つ変化だからです。
学習が進んでいるかどうかを判断するには、対数目盛りのほうが適しています。
学習率の探索
ハイパーパラメータを探索する際にも、対数が使われます。
学習率の候補を考えるとき、次のように等間隔で選ぶのは適切ではありません。
学習率にとって重要なのは、桁の違いです。0.1と0.2の違いより、0.1と0.001の違いのほうが、はるかに大きな影響を持ちます。
したがって、対数スケールで等間隔に選びます。
NumPyには、この選び方を行う関数があります。
import numpy as np
candidates = np.logspace(-5, -1, 5)
print(candidates)
同じ考え方は、正則化の強さや、バッチサイズの探索にも使われます。
交差エントロピー誤差における対数
ここまでの理由が、実際に組み合わさっている例として、交差エントロピー誤差を見ておきます。
式の確認
分類問題で最もよく使われる損失関数です。
は正解のラベル、
はモデルが予測した確率です。
正解ラベルがOne-Hot表現であれば、正解のクラス以外は0になるため、実質的に次の形になります。
なぜこの形なのか
理由1で扱った、尤度の最大化から導かれます。
すべてのデータが正しく分類される確率、つまり尤度を最大化したい。
これに対数を取り、掛け算を足し算に変えます。
損失として「小さくしたい値」にするため、マイナスをつけます。
これが、交差エントロピー誤差です。
罰の与え方という解釈
別の見方もできます。
正解の確率を0.9と予測した場合、損失は次のようになります。
正解の確率を0.1と予測した場合、損失は次のようになります。
正解の確率を0.001と予測した場合、損失は次のようになります。
自信を持って間違えるほど、損失が急激に大きくなります。
「確信を持って外した予測」に、強い罰を与える構造になっているのです。

五つの理由の整理
| 理由 | 目的 | 主な登場場面 |
|---|---|---|
| 掛け算を足し算に | 微分と計算の簡略化 | 対数尤度、交差エントロピー誤差 |
| 数値の安定化 | アンダーフローとオーバーフローの回避 | Softmax、LogSumExp |
| 範囲の解放 | 0から1を実数全体へ | ロジット変換、ロジスティック回帰 |
| 分布の整形 | 偏りの是正、掛け算関係の線形化 | 前処理としてのデータ変換 |
| 桁の比較 | 桁違いの値を扱いやすく | 損失グラフ、ハイパーパラメータ探索 |
同じ対数という道具が、これだけ異なる目的で使われています。
「なぜlogを取るのか」という問いに対しては、「場面によって理由が違う」というのが、正確な答えです。
logを見かけたら、五つのうちどれに該当するかを判断してください。それが分かれば、その式が何をしようとしているかが読み取れます。
Pythonで確認する
import numpy as np
import math
print("=== 理由1:掛け算が足し算になる ===")
probs = np.array([0.9, 0.8, 0.95, 0.7, 0.85])
product = np.prod(probs)
log_sum = np.sum(np.log(probs))
print(f"確率: {probs}")
print(f"掛け算の結果: {product:.6f}")
print(f"その対数: {np.log(product):.6f}")
print(f"対数の和: {log_sum:.6f}")
print(f"一致するか: {np.isclose(np.log(product), log_sum)}")
print("\n=== 最大値の位置は変わらない ===")
def likelihood(theta):
return np.exp(-(theta - 2)**2)
thetas = np.linspace(0, 4, 401)
L = likelihood(thetas)
logL = np.log(L)
print(f"尤度が最大になるθ: {thetas[np.argmax(L)]:.4f}")
print(f"対数尤度が最大になるθ: {thetas[np.argmax(logL)]:.4f}")
print("同じ位置で最大になります。")
print("\n=== 理由2:アンダーフローの回避 ===")
p = 0.001
n = 1000
naive = p ** n
print(f"{p}を{n}回掛けた結果(そのまま): {naive}")
print(f"→ 0になってしまい、情報が失われました")
log_result = n * math.log(p)
print(f"対数の世界で計算: {log_result:.2f}")
print(f"→ 扱える範囲の数値です")
print("\n--- 何回掛けると0になるか ---")
for n in [100, 200, 300, 320, 350]:
val = 0.001 ** n
print(f" {n:>4}回: {val:.3e}" if val > 0 else f" {n:>4}回: 0 になりました")
print("\n=== 理由2:オーバーフローの回避 ===")
def naive_softmax(x):
exp_x = np.exp(x)
return exp_x / exp_x.sum()
def stable_softmax(x):
shifted = x - np.max(x)
exp_x = np.exp(shifted)
return exp_x / exp_x.sum()
logits = np.array([1000.0, 1001.0, 1002.0])
print(f"入力: {logits}")
with np.errstate(over='ignore', invalid='ignore'):
print(f"素朴な実装: {naive_softmax(logits)}")
print(f"安定な実装: {stable_softmax(logits).round(6)}")
print("\n=== LogSumExp ===")
def logsumexp_naive(x):
return np.log(np.sum(np.exp(x)))
def logsumexp_stable(x):
m = np.max(x)
return m + np.log(np.sum(np.exp(x - m)))
x = np.array([1000.0, 1001.0, 1002.0])
with np.errstate(over='ignore', invalid='ignore'):
print(f"素朴な実装: {logsumexp_naive(x)}")
print(f"安定な実装: {logsumexp_stable(x):.4f}")
print("\n=== 理由3:ロジット変換 ===")
print(f"{'確率p':>8} | {'オッズ':>12} | {'ロジット':>12}")
print("-" * 38)
for p in [0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99]:
odds = p / (1 - p)
logit = math.log(odds)
print(f"{p:>8.2f} | {odds:>12.4f} | {logit:>12.4f}")
print("\n0から1の範囲が、実数全体に広がっています。")
print("\n=== ロジットからシグモイドへ ===")
def sigmoid(z):
return 1 / (1 + np.exp(-z))
print(f"{'ロジットz':>10} | {'確率p':>10}")
print("-" * 24)
for z in [-4, -2, -1, 0, 1, 2, 4]:
print(f"{z:>10} | {sigmoid(z):>10.4f}")
print("\n=== 理由4:偏った分布の整形 ===")
np.random.seed(0)
# 右に裾を引く分布を生成
income = np.random.lognormal(mean=6.0, sigma=1.0, size=10000)
print("元のデータ(年収のような分布)")
print(f" 平均: {income.mean():>12.2f}")
print(f" 中央値: {np.median(income):>10.2f}")
print(f" 最大値: {income.max():>10.2f}")
print(f" 歪度の目安(平均と中央値の比): {income.mean()/np.median(income):.4f}")
log_income = np.log(income)
print("\n対数変換後")
print(f" 平均: {log_income.mean():>12.4f}")
print(f" 中央値: {np.median(log_income):>10.4f}")
print(f" 最大値: {log_income.max():>10.4f}")
print(f" 平均と中央値の比: {log_income.mean()/np.median(log_income):.4f}")
print("\n平均と中央値が近づき、分布が対称に近づいています。")
print("\n=== 0を含むデータへの対処 ===")
data = np.array([0, 1, 10, 100, 1000])
print(f"元のデータ: {data}")
with np.errstate(divide='ignore'):
print(f"log(x): {np.log(data)}")
print(f"log1p(x): {np.log1p(data).round(4)}")
print("log1pを使えば、0を含んでいても計算できます。")
print("\n=== 掛け算の関係が線形になる ===")
x = np.array([1, 2, 4, 8, 16, 32], dtype=float)
y = 3 * x ** 2 # y = 3x^2
print(f"{'x':>6} | {'y':>10} | {'log(x)':>10} | {'log(y)':>10}")
print("-" * 42)
for xi, yi in zip(x, y):
print(f"{xi:>6.0f} | {yi:>10.0f} | {math.log(xi):>10.4f} | {math.log(yi):>10.4f}")
# log(y) と log(x) の関係を線形回帰で確認
coeffs = np.polyfit(np.log(x), np.log(y), 1)
print(f"\nlog(y) = {coeffs[0]:.4f} * log(x) + {coeffs[1]:.4f}")
print(f"傾きが2に近いことから、y = a * x^2 の関係が読み取れます。")
print("\n=== 理由5:桁の比較 ===")
print("学習の損失の推移(模擬データ)")
losses = [100.0, 30.0, 8.0, 2.0, 0.5, 0.1, 0.03, 0.008, 0.002, 0.0005]
print(f"{'エポック':>8} | {'損失':>10} | {'log10(損失)':>14} | {'前回比':>10}")
print("-" * 50)
for i, loss in enumerate(losses):
ratio = losses[i-1] / loss if i > 0 else float('nan')
ratio_str = f"{ratio:.2f}倍" if i > 0 else "-"
print(f"{i:>8} | {loss:>10.4f} | {math.log10(loss):>14.4f} | {ratio_str:>10}")
print("\n通常の目盛りでは後半の変化が見えませんが、")
print("対数を取ると、一定のペースで減少していることが分かります。")
print("\n=== 学習率の候補を対数スケールで選ぶ ===")
linear_candidates = np.linspace(0.00001, 0.1, 5)
log_candidates = np.logspace(-5, -1, 5)
print(f"等間隔で選んだ場合: {linear_candidates}")
print(f"対数スケールで選んだ場合: {log_candidates}")
print("\n対数スケールのほうが、桁を広くカバーできます。")
print("\n=== 交差エントロピー誤差における対数 ===")
print("正解クラスの予測確率と、そのときの損失")
print(f"{'予測確率':>10} | {'-log(p)':>10} | {'評価':>16}")
print("-" * 42)
for p in [0.99, 0.9, 0.5, 0.1, 0.01, 0.001]:
loss = -math.log(p)
if p >= 0.9:
comment = "ほぼ正解"
elif p >= 0.5:
comment = "まあまあ"
elif p >= 0.1:
comment = "外している"
else:
comment = "確信を持って誤り"
print(f"{p:>10.3f} | {loss:>10.4f} | {comment:>16}")
print("\n自信を持って間違えるほど、損失が急激に増えます。")
print("\n=== PyTorchでの実装確認 ===")
try:
import torch
import torch.nn.functional as F
logits = torch.tensor([[100.0, 101.0, 102.0]])
print("--- 分けて計算した場合 ---")
probs = F.softmax(logits, dim=1)
log_probs_split = torch.log(probs)
print(f"log(softmax(x)): {log_probs_split.numpy().round(6)}")
print("\n--- まとめて計算した場合 ---")
log_probs_together = F.log_softmax(logits, dim=1)
print(f"log_softmax(x): {log_probs_together.numpy().round(6)}")
print("\n--- 極端な値での比較 ---")
extreme = torch.tensor([[1000.0, 0.0, -1000.0]])
probs_e = F.softmax(extreme, dim=1)
print(f"softmax後にlog: {torch.log(probs_e).numpy()}")
print(f"log_softmax: {F.log_softmax(extreme, dim=1).numpy()}")
print("\n分けて計算すると、マイナス無限大が発生します。")
except ImportError:
print("PyTorchがインストールされていないため、スキップします。")
初学者がつまずきやすい点
つまずき1:すべてのlogに同じ理由があると思う
冒頭で述べたとおり、場面によって理由が異なります。
前処理での対数変換と、損失関数の中の対数は、まったく別の目的です。
前者は、データの分布を整えるためです。後者は、確率の掛け算を足し算に変えるためです。
混同すると、「なぜここでlogを取るのか」が理解できなくなります。
つまずき2:対数を取ると値が変わることを気にしすぎる
対数を取れば、値そのものは変わります。
しかし、最大化や最小化の問題では、最適な位置が変わらなければ問題ありません。
対数が単調増加であるという性質が、これを保証しています。
つまずき3:0以下の値に対数を取ろうとする
対数は、正の値にしか定義されません。
前処理で対数変換を行う際、データに0や負の値が含まれていないかを、必ず確認してください。
0が含まれる場合は、1を足してから対数を取る方法が使われます。負の値が含まれる場合は、そもそも対数変換が適切でない可能性を検討してください。
つまずき4:底の違いを気にしすぎる
自然対数、常用対数、2を底とする対数。どれを使うかで、値は定数倍だけ変わります。
最大化や最小化の問題では、定数倍の違いは結果に影響しません。
情報量を扱う場面ではビット単位に対応する2を底とする対数、それ以外では微分が扱いやすい自然対数が使われることが多い、という程度の理解で十分です。
演習
演習1:掛け算と足し算の対応を確認する
次の確率について、掛け算の結果と、対数の和を計算し、対応を確認してください。
、
、
を使ってください。
演習2:ロジットを計算する
次の確率について、オッズとロジットをそれぞれ計算してください。
二つの結果の間に、どのような関係があるか説明してください。
演習3:交差エントロピー誤差を計算する
3クラス分類で、正解が2番目のクラスだとします。
モデルの予測確率が のとき、交差エントロピー誤差を計算してください。
また、予測確率が のときも計算し、比較してください。
、
を使ってください。
演習4:対数変換の効果を確認する
import numpy as np
np.random.seed(42)
# 右に大きく裾を引くデータ
data = np.random.lognormal(mean=5, sigma=1.5, size=5000)
print("=== 元のデータ ===")
print(f"最小値: {data.min():>12.2f}")
print(f"25%点: {np.percentile(data, 25):>12.2f}")
print(f"中央値: {np.median(data):>12.2f}")
print(f"75%点: {np.percentile(data, 75):>12.2f}")
print(f"最大値: {data.max():>12.2f}")
print(f"平均: {data.mean():>12.2f}")
print("\n=== 対数変換後 ===")
log_data = np.log(data)
print(f"最小値: {log_data.min():>12.4f}")
print(f"25%点: {np.percentile(log_data, 25):>12.4f}")
print(f"中央値: {np.median(log_data):>12.4f}")
print(f"75%点: {np.percentile(log_data, 75):>12.4f}")
print(f"最大値: {log_data.max():>12.4f}")
print(f"平均: {log_data.mean():>12.4f}")
print("\n=== ヒストグラムを文字で表示 ===")
def text_histogram(values, bins=15, width=50):
counts, edges = np.histogram(values, bins=bins)
max_count = counts.max()
for i in range(bins):
bar = "#" * int(counts[i] / max_count * width)
print(f"{edges[i]:>10.2f} | {bar}")
print("元のデータ:")
text_histogram(data)
print("\n対数変換後:")
text_histogram(log_data)
演習の解答例と解説
演習1の解答は、次のとおりです。
掛け算の結果は、次のようになります。
対数の和は、次のようになります。
検算します。
小数点以下の丸め誤差を除けば、一致しています。
掛け算が足し算に対応していることが確認できます。
演習2の解答は、次のとおりです。
の場合。
オッズは です。
ロジットは です。
の場合。
オッズは です。
ロジットは です。
二つの結果は、符号だけが逆になっています。
これは偶然ではありません。0.8と0.2は、0.5を挟んで対称な位置にあります。ロジット変換では、確率0.5が0に対応し、そこから対称な確率は、符号が反転した同じ大きさのロジットになります。
演習3の解答は、次のとおりです。
予測確率が の場合、正解である2番目の確率は0.7です。
予測確率が の場合、正解である2番目の確率は0.2です。
後者の損失は、前者の約4.5倍です。
注目すべき点は、後者では1番目のクラスに0.6という高い確率を割り当てていることです。自信を持って間違えているため、大きな罰が与えられています。
なお、正解以外のクラスの確率は、計算に一切影響していません。One-Hot表現では、正解以外のラベルが0であるため、掛け算の結果が0になるからです。
演習4では、元のデータが極端に右に裾を引いていること、そして対数変換によって、より対称的な分布に近づくことが確認できます。
文字で表示したヒストグラムを見比べると、変換前は左端に集中していた分布が、変換後は中央付近を頂点とする山型に近づいているはずです。
まとめ
機械学習で対数を取る理由は、一つではありません。場面によって、目的が異なります。
第一の理由は、掛け算を足し算に変えることです。確率の積として表される尤度を、対数を取ることで和に変換し、微分と計算を簡単にします。対数は単調増加であるため、最大値を取るパラメータの位置は変わりません。交差エントロピー誤差の式も、この考え方から導かれます。
第二の理由は、数値計算の安定化です。多数の確率を掛け合わせると、コンピュータが表現できる下限を下回り、0になってしまいます。対数の世界であれば、扱いやすい大きさの値になります。逆に、指数関数によるオーバーフローに対しては、最大値を引いてから計算するという工夫が使われます。
第三の理由は、値の範囲を制限から解放することです。0から1に限られる確率を、ロジット変換によって実数全体に写します。これにより、線形結合の出力と確率を結びつけられるようになります。ロジスティック回帰の基礎です。
第四の理由は、偏った分布の整形です。年収や売上のように右に裾を引くデータは、対数変換によって、より対称的な分布に近づきます。また、掛け算やべき乗の関係を、線形の関係に変換する効果もあります。0が含まれる場合は、1を足してから対数を取ってください。
第五の理由は、桁の違うものを比べやすくすることです。損失のグラフを対数目盛りで描けば、後半の細かな変化も見えるようになります。学習率のようなハイパーパラメータを探索する際も、対数スケールで候補を選びます。
logを見かけたら、この五つのうちどれに該当するかを考えてください。それが分かれば、その式が何をしようとしているかが読み取れるようになります。
次の学習ステップとして、まずは手元で、確率を何度も掛け合わせるとどこで0になるかを確認してみてください。そのうえで、同じ計算を対数の世界で行い、値が扱える範囲に収まることを確認すると、数値安定性の重要さが実感できます。さらに、実際のデータセットで年収や売上のような列を探し、対数変換前後のヒストグラムを比較してみると、前処理としての効果が目で見て理解できるようになります。
セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。
投稿者プロフィール

- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
海外放浪の末、2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。
学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。
最新の投稿
新人エンジニア研修講師2026年8月12日第16章 なぜGPUで速くなるのか、そして次の一歩
新人エンジニア研修講師2026年8月12日第15章 誤差逆伝播に現れる行列の転置
新人エンジニア研修講師2026年8月12日第14章 勾配ベクトルとは何か
新人エンジニア研修講師2026年8月11日第13章 線形だけでは足りない理由

