LoginSignup
14
13

More than 1 year has passed since last update.

相関係数Rと決定係数R2の違いが!「言葉」でなく「心」で理解できた!

Last updated at Posted at 2023-03-16

どうも、昨夜9時頃Twitterアカウントが永久凍結した Ikemen Mas Kot です。過去記事「相関係数が0.63の散布図を作成する」の続編を思いついたので、やってみました。今回のテーマは

相関係数Rと決定係数R2の違いが!「言葉」でなく「心」で理解できた!

で行きたいと思います。それぞれの定義については、ググって調べてください(放置系)。「言葉」でなく「心」で理解するために、今回もOptunaでプロットを自作します。

目的関数の設計

import numpy as np
from scipy.stats import pearsonr
from sklearn.metrics import r2_score


class Objective:  # Optuna で最適化する目的関数
    def __init__(
        self,
        n_data=20,  # データ数
        target_r=0.95,  # 目標とするピアソン相関係数の値
        delta=0.005,  # 許容する誤差の最大値
        direction="minimize",  # 最適化する方向
    ):
        self.n_data = n_data  # データ数
        self.target_r = target_r  # 目標とするピアソン相関係数の値
        self.delta = delta  # 許容する誤差の最大値
        self.direction = direction  # 最適化する方向

        # 最大化・最小化の目的に合わせて初期値を設定する
        if self.direction == "maximize":
            self.best_score = -530000
            self.penalty = -530000
        else:
            self.best_score = 530000  # フリーザ(初期形態)の戦闘力
            self.penalty = 530000  # フリーザ(初期形態)の戦闘力

        self.best_X = None  # ベストX座標を記録する場所
        self.best_Y = None  # ベストY座標を記録する場所

    def __call__(self, trial):  # Optuna に呼ばれるときはこれが実行される

        # X座標は均等に決め打ちする
        self.X = np.linspace(0.05, 0.95, self.n_data)

        # Y座標を Optuna に最適化してもらう
        self.Y = []
        for i in range(self.n_data):
            self.Y.append(trial.suggest_float("y{}".format(i), 0, 1))

        # ピアソンの相関係数を r とする
        r, p = pearsonr(self.X, self.Y)

        # 決定係数を r2 とする
        r2 = r2_score(self.X, self.Y)

        # ピアソンの相関係数が目標値に十分近いとき、決定係数をスコアとする
        if self.target_r - self.delta < r and r < self.target_r + self.delta:
            score = r2
        else:  # ピアソンの相関係数が目標値から離れすぎているとき、その距離に応じてペナルティを与える
            score = abs(self.target_r - r) * self.penalty

        # 今までの記録よりも良いものが得られたとき、ベスト結果を更新する
        if (self.direction == "maximize" and self.best_score < score) or (
            self.direction == "minimize" and self.best_score > score
        ):
            self.best_score = score
            self.best_X = self.X
            self.best_Y = self.Y
        return score

R2最大化

ピアソンの相関係数 R = 0.9 を目標とし、決定係数 R2 をできるだけ最大化した計算例がこちらです。

import optuna

optuna.logging.set_verbosity(optuna.logging.WARN)  # 途中経過メッセージを省略する

n_trials = 1000  # 試行回数
objective = Objective(target_r=0.90, direction="maximize")  # 目的関数を設定
study = optuna.create_study(directions=["maximize"])  # Optuna の設定
study.optimize(objective, n_trials=n_trials, show_progress_bar=True)  # 最適化計算実行
/usr/local/lib/python3.9/site-packages/optuna/progress_bar.py:49: ExperimentalWarning: Progress bar is experimental (supported from v1.2.0). The interface can change in the future.
  self._init_valid()



  0%|          | 0/1000 [00:00<?, ?it/s]
import matplotlib.pyplot as plt

plt.title(
    "R={:.3f}, R2={:.3f}".format(
        pearsonr(objective.best_X, objective.best_Y)[0],
        r2_score(objective.best_X, objective.best_Y),
    )
)
plt.scatter(objective.best_X, objective.best_Y)
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.grid()
plt.savefig("0.png")

output_5_0.png

なるほど。続いて、ピアソンの相関係数 R = 0.9 を目標とし、決定係数 R2 をできるだけ最小化した計算例がこちらです。

import optuna

optuna.logging.set_verbosity(optuna.logging.WARN)  # 途中経過メッセージを省略する

objective = Objective(target_r=0.90, direction="minimize")
study = optuna.create_study(directions=["minimize"])
study.optimize(objective, n_trials=n_trials, show_progress_bar=True)
/usr/local/lib/python3.9/site-packages/optuna/progress_bar.py:49: ExperimentalWarning: Progress bar is experimental (supported from v1.2.0). The interface can change in the future.
  self._init_valid()



  0%|          | 0/1000 [00:00<?, ?it/s]
import matplotlib.pyplot as plt

plt.title(
    "R={:.3f}, R2={:.3f}".format(
        pearsonr(objective.best_X, objective.best_Y)[0],
        r2_score(objective.best_X, objective.best_Y),
    )
)
plt.scatter(objective.best_X, objective.best_Y)
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.grid()
plt.savefig("1.png")

output_8_0.png

データ数を増やしていくぜ

それでは、データ数を増やしていきたいと思います。計算に時間がかかるので、途中で何かの原因で計算が止まってしまうようなことがあれば、再計算するのが面倒です。そのようなときに備えて、Optunaに計算履歴を残してもらうと大変便利です。

for n_data in [10, 20, 100, 200, 1000]:  # データ数を増やしていく

    # データ数ごとに結果表示をする
    fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(20, 8))

    # 目標とするピアソン相関係数Rの値を変えていく
    for j, target_r in enumerate([0.9, 0.7, 0.5, 0.25, 0.0]):

        # 決定係数R2を最大化した計算例、最小化した計算例をそれぞれ算出する
        for i, direction in enumerate(["maximize", "minimize"]):

            # 目的関数を設定する
            objective = Objective(n_data=n_data, target_r=target_r, direction=direction)

            # 計算履歴を残すデータベースの名前を決める
            study_name = "{}-{}-{}".format(n_data, target_r, direction)

            # 計算履歴を残せる形で、Optuna の study を設定する
            study = optuna.create_study(
                directions=[direction],
                study_name=study_name,
                storage="sqlite:///" + study_name + ".sql",
                load_if_exists=True,
            )

            # 最適化計算
            study.optimize(objective, n_trials=n_trials, show_progress_bar=True)

            # 1つのパネルに計算結果を描画する
            axes[i][j].set_title(
                "n_data={}, R={:.3f}, R2={:.3f}".format(
                    n_data,
                    pearsonr(objective.best_X, objective.best_Y)[0],
                    r2_score(objective.best_X, objective.best_Y),
                )
            )
            axes[i][j].scatter(objective.best_X, objective.best_Y)
            axes[i][j].set_xlim([0, 1])
            axes[i][j].set_ylim([0, 1])
            axes[i][j].grid()

    # データ数ごとに結果表示をして画像を保存する
    plt.savefig("{}.png".format(n_data))
    plt.show()  # 画像を表示する

n_data = 10

image.png

n_data = 20

image.png

わかるか?え?RとR2の関係が...

n_data = 100

image.png

さすがに n_data = 100 ともなると最適化が難しくなってきましたね...試行回数がまだ足りないかな...もっとデータ数が大きいときはもっと酷い結果だったので省略ッ

見つけたぜ 関連記事だ

14
13
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13