NumPy random.multivariate_normalのエラーシューティング完全ガイド:原因と対策

2025-03-21

多次元正規分布とは?

多次元正規分布は、複数の変数(次元)が互いに関連し合うような確率分布です。例えば、2次元であれば、身長と体重の組み合わせがどのような確率で現れるか、といった情報を表現できます。

random.multivariate_normal()の基本的な使い方

random.multivariate_normal()は、以下の引数を取ります。

  • size: 生成する乱数のサンプル数。省略すると、単一のサンプルが生成されます。
  • cov: 共分散行列。各次元の分散と、次元間の共分散を格納した行列です。
  • mean: 平均ベクトル。各次元の平均値を格納した配列です。

具体的な例

例えば、2次元の多変量正規分布から乱数を生成したい場合、以下のようにします。

import numpy as np

# 平均ベクトル
mean = np.array([0, 0])

# 共分散行列
cov = np.array([[1, 0.5], [0.5, 1]])

# 乱数を生成
samples = np.random.multivariate_normal(mean, cov, size=100)

print(samples)

コードの説明

  1. mean = np.array([0, 0]): 平均ベクトルを定義しています。この例では、各次元の平均値は0です。
  2. cov = np.array([[1, 0.5], [0.5, 1]]): 共分散行列を定義しています。対角成分は各次元の分散、非対角成分は次元間の共分散を表します。
  3. samples = np.random.multivariate_normal(mean, cov, size=100): 平均ベクトルmean、共分散行列covに従う多変量正規分布から、100個のサンプルを生成します。
  4. print(samples): 生成された乱数を表示します。

重要なポイント

  • 多次元正規分布は、統計学や機械学習など、様々な分野で広く利用されています。
  • size引数によって、生成するサンプル数を制御できます。
  • 共分散行列は、正定値行列である必要があります。
  • 物理学における粒子の運動のシミュレーション
  • 金融工学における資産価格のシミュレーション
  • 機械学習における教師なし学習の生成モデル


よくあるエラーとトラブルシューティング

    • 原因
      平均ベクトル(mean)または共分散行列(cov)にNaN(Not a Number)やinf(無限大)が含まれている場合に発生します。
    • 対処法
      • meancovの値をチェックし、NaNinfが含まれていないか確認してください。
      • データの欠損値処理や外れ値処理を見直してください。
  1. LinAlgError: singular matrix (線形代数エラー:特異行列)

    • 原因
      共分散行列(cov)が特異行列(正定値行列でない)である場合に発生します。つまり、行列の逆行列が存在しない状態です。
    • 対処法
      • 共分散行列が正定値行列であることを確認してください。正定値行列とは、すべての固有値が正の行列のことです。
      • 共分散行列の計算に使用したデータに線形従属な列がないか確認してください。
      • 共分散行列に微小な値を加えて、正定値行列に近づける方法もあります(例:cov + 1e-6 * np.eye(cov.shape[0]))。ただし、この方法はデータの性質を変化させる可能性があるため、慎重に行ってください。
  2. ValueError: mean must be a 1 dimensional array (値エラー:平均は1次元配列でなければなりません)

    • 原因
      平均ベクトル(mean)が1次元配列でない場合に発生します。
    • 対処法
      • meannp.array([...])のように1次元配列として定義してください。
  3. ValueError: cov must be a 2 dimensional array (値エラー:共分散は2次元配列でなければなりません)

    • 原因
      共分散行列(cov)が2次元配列でない場合に発生します。
    • 対処法
      • covnp.array([[...], [...]])のように2次元配列として定義してください。
  4. ValueError: mean and cov must have the same length (値エラー:平均と共分散は同じ長さでなければなりません)

    • 原因
      平均ベクトル(mean)の長さと共分散行列(cov)の次元が一致しない場合に発生します。
    • 対処法
      • meancovの次元が一致するように調整してください。
  5. 生成される乱数が期待と異なる場合

    • 原因
      平均ベクトルや共分散行列の設定が誤っている可能性があります。
    • 対処法
      • 平均ベクトルと共分散行列の値を確認し、期待する分布になっているか確認してください。
      • 生成された乱数をヒストグラムなどで可視化し、分布を確認してください。
      • 共分散行列の相関関係を確認するために、相関行列を計算してみましょう。
      • サンプルサイズを大きくして、分布の安定性を確認してください。

デバッグのヒント

  • NumPyのドキュメントやオンラインフォーラムを参照してください。
  • エラーメッセージをよく読み、原因を特定してください。
  • 生成された乱数を可視化し、分布を確認してください。
  • np.linalg.eig()を使用して、共分散行列の固有値を計算し、正定値行列であることを確認してください。
  • print()文を使用して、meancovの値を確認してください。


例1:2次元正規分布からの乱数生成と可視化

import numpy as np
import matplotlib.pyplot as plt

# 平均ベクトル
mean = np.array([0, 0])

# 共分散行列
cov = np.array([[1, 0.5], [0.5, 1]])

# 乱数を生成
samples = np.random.multivariate_normal(mean, cov, size=500)

# 散布図で可視化
plt.figure(figsize=(8, 6))
plt.scatter(samples[:, 0], samples[:, 1], alpha=0.7)
plt.title("2次元正規分布からの乱数")
plt.xlabel("X軸")
plt.ylabel("Y軸")
plt.grid(True)
plt.show()

コードの説明

  1. meancovで2次元正規分布の平均と共分散を設定します。
  2. np.random.multivariate_normal()で500個の乱数を生成します。
  3. matplotlib.pyplotを使って、生成された乱数を散布図で可視化します。
  4. グラフのタイトル、軸ラベル、グリッドを設定して表示します。

例2:異なる共分散行列による分布の変化

import numpy as np
import matplotlib.pyplot as plt

# 平均ベクトル
mean = np.array([0, 0])

# 共分散行列のリスト
covs = [
    np.array([[1, 0], [0, 1]]),  # 独立な標準正規分布
    np.array([[1, 0.8], [0.8, 1]]), # 正の相関
    np.array([[1, -0.8], [-0.8, 1]]), #負の相関
    np.array([[2, 0], [0, 0.5]]) #分散の大きさが異なる
]

# 各共分散行列で乱数を生成し、可視化
plt.figure(figsize=(12, 8))
for i, cov in enumerate(covs):
    samples = np.random.multivariate_normal(mean, cov, size=500)
    plt.subplot(2, 2, i + 1)
    plt.scatter(samples[:, 0], samples[:, 1], alpha=0.7)
    plt.title(f"共分散行列 {i+1}")
    plt.xlabel("X軸")
    plt.ylabel("Y軸")
    plt.grid(True)

plt.tight_layout()
plt.show()

コードの説明

  1. 複数の共分散行列covsをリストに格納します。
  2. forループで各共分散行列に対して乱数を生成し、subplotを使って複数のグラフを1つのウィンドウに表示します。
  3. 各グラフのタイトルに共分散行列の番号を表示し、分布の違いを比較できるようにします。
  4. tight_layout()を使用して、グラフのレイアウトを調整します。

例3:3次元正規分布からの乱数生成と3Dプロット

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 平均ベクトル
mean = np.array([0, 0, 0])

# 共分散行列
cov = np.array([[1, 0.5, 0.2], [0.5, 1, 0.3], [0.2, 0.3, 1]])

# 乱数を生成
samples = np.random.multivariate_normal(mean, cov, size=500)

# 3Dプロットで可視化
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(samples[:, 0], samples[:, 1], samples[:, 2], alpha=0.7)
ax.set_title("3次元正規分布からの乱数")
ax.set_xlabel("X軸")
ax.set_ylabel("Y軸")
ax.set_zlabel("Z軸")
plt.show()
  1. 3次元の平均ベクトルmeanと共分散行列covを設定します。
  2. mpl_toolkits.mplot3dからAxes3Dをインポートして、3Dプロットを作成します。
  3. ax.scatter()で3次元の散布図を表示します。
  4. 軸ラベルとタイトルを設定して、3Dプロットを表示します。


コレスキー分解を用いた方法

random.multivariate_normal()の内部では、コレスキー分解を用いて多変量正規分布の乱数を生成しています。この方法を直接実装することも可能です。

import numpy as np

def multivariate_normal_chol(mean, cov, size=1):
    """コレスキー分解を用いた多変量正規分布の乱数生成"""
    n_dim = len(mean)
    L = np.linalg.cholesky(cov)  # コレスキー分解
    z = np.random.normal(size=(size, n_dim)) # 標準正規分布からの乱数
    return mean + np.dot(z, L.T) # 変換

# 例
mean = np.array([0, 0])
cov = np.array([[1, 0.5], [0.5, 1]])
samples = multivariate_normal_chol(mean, cov, size=100)
print(samples)

説明

  1. np.linalg.cholesky(cov)で共分散行列covのコレスキー分解を行い、下三角行列Lを取得します。
  2. np.random.normal()で標準正規分布に従う乱数zを生成します。
  3. mean + np.dot(z, L.T)zを変換し、多変量正規分布に従う乱数を生成します。

利点

  • 特定の状況下では、パフォーマンスが向上する可能性があります。
  • random.multivariate_normal()の内部処理を理解するのに役立ちます。

欠点

  • コレスキー分解が失敗する(共分散行列が正定値行列でない)場合、エラーが発生します。
  • コードが少し複雑になります。

固有値分解を用いた方法

固有値分解を用いて多変量正規分布の乱数を生成することも可能です。

import numpy as np

def multivariate_normal_eig(mean, cov, size=1):
    """固有値分解を用いた多変量正規分布の乱数生成"""
    n_dim = len(mean)
    eigenvalues, eigenvectors = np.linalg.eigh(cov) # 固有値分解
    sqrt_eigenvalues = np.sqrt(eigenvalues)
    L = eigenvectors @ np.diag(sqrt_eigenvalues)
    z = np.random.normal(size=(size, n_dim))
    return mean + np.dot(z, L.T)

# 例
mean = np.array([0, 0])
cov = np.array([[1, 0.5], [0.5, 1]])
samples = multivariate_normal_eig(mean, cov, size=100)
print(samples)

説明

  1. np.linalg.eigh(cov)で共分散行列covの固有値分解を行い、固有値と固有ベクトルを取得します。
  2. 固有値の平方根を計算し、対角行列を作成します。
  3. 固有ベクトルと対角行列を掛け合わせて変換行列Lを作成します。
  4. 標準正規分布に従う乱数zを生成し、Lで変換します。

利点

  • コレスキー分解が失敗する場合でも、固有値分解は安定して動作する可能性があります。

欠点

  • コレスキー分解よりも計算コストが高くなる場合があります。

SciPyを用いた方法

SciPyのscipy.stats.multivariate_normalを使用することもできます。

import numpy as np
from scipy.stats import multivariate_normal

# 例
mean = np.array([0, 0])
cov = np.array([[1, 0.5], [0.5, 1]])
samples = multivariate_normal.rvs(mean, cov, size=100)
print(samples)

説明

  1. scipy.stats.multivariate_normalをインポートします。
  2. multivariate_normal.rvs(mean, cov, size)で多変量正規分布に従う乱数を生成します。

利点

  • SciPyの他の統計関数と連携しやすいです。
  • より高レベルなインターフェースを提供し、コードが簡潔になります。

欠点

  • NumPyのみを使用する場合よりも依存ライブラリが増えます。

逐次的な乱数生成

多変量正規分布の条件付き分布を用いて、逐次的に乱数を生成することも可能です。この方法は、特定の構造を持つ多変量正規分布に対して有効です。

  • 特定の構造を持つ多変量正規分布に対しては、逐次的な乱数生成が有効です。
  • より高レベルな統計処理を行う場合は、SciPyのscipy.stats.multivariate_normalを使用します。
  • 内部処理を理解したい場合や、特定の最適化が必要な場合は、コレスキー分解や固有値分解を直接実装します。
  • 単純な乱数生成にはrandom.multivariate_normal()が最適です。