その他

回帰分析で積雪深を予測してみた!その2

この記事は約10分で読めます。

前回のおさらい

こんにちは、よーすけです!

前回は気象庁が公開している過去の気象データをもとに札幌市の積雪深を予測するプログラムを作成してみました。(先週の記事をご覧になっていない方は以下のリンクからぜひ!)

Pythonのseabornというライブラリを用いて、積雪深に最も関係がある気象情報が何かをヒートマップで可視化したところ降雪量が関係があるという結果になったので、図1のように入力が降雪量で出力が積雪深となるよう回帰分析で求めました。

図1

その結果、モデルの当てはまり度である決定係数が0.14と低い値となったため、降雪量だけでは積雪深を求めるには足りないと結論付けました。

今週の取り組み

今週は、表1のように収集したデータの気温(temperature)や湿度(humidity)など複数の気象要素を入力データとして用い、重回帰分析を用いて札幌市の積雪深を予測していこうと思います。

先週との違いとして、説明変数が一つの場合は単回帰分析でしたが、複数ある場合は重回帰分析が一般的です。

また、重回帰分析は複数の説明変数を用いることでより多くの情報を反映できるため、予測精度の向上や実用性の面で優れています。

一方で、計算が複雑で理解しづらいというデメリットがあるようで、現在僕も理解しきれていません。

ですので一緒に学んでいきましょう!

monthdayatmospheretemperaturehumidityprecipitationsnow_fallingsnow_depth
1261008.95.282000
127995.37.779300
1281009.61.6620.522
1291009.77.7834.500
12101017.60.357000
12111022-1.964011
12121014.1-0.778766
12131018.2-1.9580.517
12141022-1.954005
表1

実装

データの標準化

冒頭でも記述したように、今回は複数の気象情報を説明変数として用い、積雪深を目的変数として予測します。

# 入力変数と出力変数の設定
int_var = ["month", "day", "atmosphere", "temperature", "humidity", "precipitation", "snow_falling"] #入力
out_var = ["snow_depth"] #出力

# 入力/出力変数をデータフレーム化
x = df[int_var]
y = df[out_var]

# データの分割
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)

# データの標準化
scaler = StandardScaler() #インスタンス
scaler.fit(x_train) #標準化
x_train_scaled = scaler.transform(x_train)

また今回は収集したデータが十分にあるため、全体のデータを訓練データとテストデータに8:2の比率で分割し、後にモデルの評価を行います。

さらに各変数のデータのばらつきを揃えるため、scikit-learnライブラリのStandardScalerで標準化をしました。

標準化を行うことで、学習の収束が安定しやすくなるため、特に重回帰分析では積極的に利用すると良いでしょう。

モデルの学習

回帰分析にはさまざまな種類の学習モデルがあるようですが、今回も前回同様にscikit-learnのLinearRegressionを使用しました。

# 回帰モデルの学習
model = LinearRegression()
model.fit(x_train_scaled, y_train)

今回の重回帰分析では、以下のような線形モデルの式が得られます。
前回扱った単回帰分析の式と構造は似ていますね。

切片と各説明変数に対応する回帰係数を推定するという点では、単回帰分析と同様の仕組みです。

重回帰分析の式

したがって、回帰分析で求めた切片と回帰係数を用いて、各説明変数の値を代入することで積雪深を予測できます。

モデルの評価

ここからは、訓練データとテストデータに分割したデータを用いて、モデルの性能を評価します。

# モデルの評価
print("\nモデルの評価:")
print("訓練データの決定係数: {:.3f}".format(model.score(x_train_scaled, y_train)))
print("テストデータの決定係数: {:.3f}".format(model.score(x_test_scaled, y_test)))

# 予測値の計算
y_pred_train = model.predict(x_train_scaled)
y_pred_test = model.predict(x_test_scaled)

# MSEの計算 #平均2乗誤差
train_mse = mean_squared_error(y_train, y_pred_train)
test_mse = mean_squared_error(y_test, y_pred_test)
print("\nMSE:")
print("訓練データMSE: {:.3f}".format(train_mse))
print("テストデータMSE: {:.3f}".format(test_mse))

#RMSEの計算 #平方根
train_rmse = np.sqrt(train_mse)
test_rmse = np.sqrt(test_mse)
print("\nRMSE:")
print("訓練データRMSE: {:.3f}".format(train_rmse))
print("テストデータRMSE: {:.3f}".format(test_rmse))
評価結果

まず、訓練データとテストデータの決定係数を比較すると、いずれも0.6以上であり、大きな差も見られないことから、過学習せずに適切に学習できていると考えられます。

しかも前回の決定係数である0.14と比べても大幅に向上しておりモデルの精度が改善されたことが確認できます。

また、モデルの予測値と実測値との差を定量的に評価するには、損失関数を用いるのが一般的です。

回帰分析では一般的にMSE(平均2乗誤差)という損失関数を用い、モデルが予測する値と実際の値の差を二乗して平均した値を指します。
なので、損失値が小さいほど精度の高いモデルを作れていると言えるでしょう。

ただし、MSEの結果が300となりとても大きいように見えますが、これは2乗することで単位がcm2となってしまいます。

そこで、MSEの平方根であるRMSE(MSEの平方根)を用いて損失値を求めます。

その結果、どちらもRMSEは18cmとなり、ある程度現実的な予測ができていることが分かります。

ですが、18cmの誤差は実用的ではありません。

誤差が大きい原因として、データにある積雪深の変動の幅が広すぎるということが考えられました。

また、今回使用したLinearRegressionは線形の回帰モデルであるため、積雪深と各気象要素の関係が必ずしも線形とは限らないため、非線形な関係を捉えきれていない可能性もあります。

そのため、次回以降からは非線形モデルでの積雪深予測を検討するとともに、データ量も増やすことで精度の向上を目指します。

結果

重回帰分析で求めた切片と各気象要素の回帰係数は以下のように得ることができました。

# 切片と回帰係数の表示
print("\nモデルのパラメータ:")
print("切片: {:.3f}".format(model.intercept_[0]))
print("\n各変数の回帰係数:")
for var, coef in zip(int_var, model.coef_[0]):
    print(f"{var}: {coef:.3f}")

while True:
    try:
        # 入力データの収集
        print("\n気象データを入力してください:")
        month = int(input("月 (1-12): "))
        day = int(input("日 (1-31): "))
        atmosphere = float(input("気圧 (hPa): "))
        temperature = float(input("気温 (°C): "))
        humidity = float(input("湿度 (%): "))
        precipitation = float(input("降水量 (mm): "))
        snow_falling = float(input("降雪量 (cm): "))
        
        # 入力データの辞書を作成
        input_data = {
            "month": month,
            "day": day,
            "atmosphere": atmosphere,
            "temperature": temperature,
            "humidity": humidity,
            "precipitation": precipitation,
            "snow_falling": snow_falling
        }
        
        # 予測の実行
        predicted_depth = predict_snow_depth(model, scaler, input_data)
        print(f"\n予測された積雪深: {predicted_depth:.2f} cm")
            
    except ValueError as e:
        print("入力が正しくありません。数値を入力してください。")
    except Exception as e:
        print(f"エラーが発生しました: {e}")
各パラメーター

ここで、求められた切片及び、回帰係数を重回帰分析の式に当てはめます。

例として2020年2月7日の各気象情報をもとに積雪深を求めます。

予測された積雪が62.35cmとなりました。

では当時どれくらい雪が積もっていたかというと、なんと64cmで誤差1.65cmです!
これだけ見ればかなり高精度ですね。

おわりに

いかがでしたでしょうか。

今週は収集したすべての気象要素を説明変数として入力し、目的変数である積雪深を重回帰分析を用いて予測しました。

その結果、予測モデルの精度が0.6以上と前回よりも精度が高くなっていることを確認できました。

一方で損失値がある程度大きいことを確認したため、非線形モデルによる積雪深の予測を検討してみます。

今回作成したソースコードは僕のgithubから使用することができます。
必要なライブラリは前回と同じですので、まだインストールしていない方はREADMEに記載の方法でインストールしてみてください!

GitHub - morishitaimpl/snowdepth_regression_multiple
Contribute to morishitaimpl/snowdepth_regression_multiple development by creating an account on GitHub.

参考URL

気象庁|過去の気象データ検索
過去の気象データ検索
[損失関数/評価関数]平均二乗誤差(MSE:Mean Squared Error)/RMSE(MSEの平方根)とは?
用語「平均二乗誤差」について説明。損失関数/評価関数の一つで、各データに対して「予測値と正解値の差(=誤差)」の二乗値を計算し、その総和をデータ数で割った値(=平均値)を表す。
【Python】scikit-learnのboston house prices datasetを使って回帰分析をしてみる