📝

Pytorchで単回帰分析を簡単実装してみた

2022/04/29に公開

はじめに

機械学習よく分からんけどとりあえず単回帰分析を簡単にやってみたい!って人向けの記事です。
機械学習についてはまた別の記事で紹介したいと思います。
また今回は単回帰分析をPytorchを使って実装します。
Pytorchの使い方も別記事で紹介したいと思います。今はこんな簡単に実装できるんだと思っていただければと思います。

早速実装していきます!

必要最低限のコードへの解説は行います。

必要ライブラリの導入

!pip install japanize_matplotlib | tail -n 1
!pip install torchviz | tail -n 1
!pip install torchinfo | tail -n 1

必要ライブラリのインポート

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
from IPython.display import display
import torch
import torch.nn as nn
import torch.optim as optim
from torchviz import make_dot

予測の目的

ボストンデータセットを使って不動産価格予測することが目的となる。
今回は単回帰分析なので入力値を「平均部屋数(RM)」を使って不動産価格を予測したいと思う。

データの準備

今回利用するデータは「ボストン・データセット」です。

# 学習用データ準備

# ライブラリのインポート
from sklearn.datasets import load_boston

# データ読み込み
boston = load_boston()

# 入力データと正解データ取得
x_org, yt = boston.data, boston.target

# 項目名リスト取得
feature_names = boston.feature_names

# 結果確認
print('元データ', x_org.shape, yt.shape)
print('項目名: ', feature_names)
元データ (506, 13) (506,)
項目名:  ['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO' 'B' 'LSTAT']

データ絞り込み (項目 RMのみ)

x = x_org[:,feature_names == 'RM']
print('絞り込み後', x.shape)
print(x[:5,:])

# 正解データ yの表示
print('正解データ')
print(yt[:5])
絞り込み後 (506, 1)
[[6.575]
 [6.421]
 [7.185]
 [6.998]
 [7.147]]
正解データ
[24.  21.6 34.7 33.4 36.2]

散布図の表示

plt.scatter(x, yt, s=10, c='b')
plt.xlabel('部屋数')
plt.ylabel('価格')
plt.title('部屋数と価格の散布図')
plt.show()

モデルの定義

機械学習モデル(予測モデル)クラス定義

まずはモデルの定義を行います。Pytorchでモデルを定義する場合、大体toruch.nn.Sequentialnn.Moduleを利用します。
今回はnn.Moduleを使います。クラス名はNetとします。引数にはnn.Module(親クラス)を指定します。
クラスの内部にはforward関数が定義されています。この関数で予測を行います。
またnn.init.constant_でパラメータ値(weight,bias)を初期値の設定にする。

class Net(nn.Module):
    def __init__(self, n_input, n_output):
        #  親クラスnn.Modulesの初期化呼び出し
        super().__init__()

        # 出力層の定義
        self.l1 = nn.Linear(n_input, n_output)   
        
        # 初期値を全部1にする
        # 「ディープラーニングの数学」と条件を合わせる目的
        nn.init.constant_(self.l1.weight, 1.0)
        nn.init.constant_(self.l1.bias, 1.0)

    # 予測関数の定義
    def forward(self, x):
        x1 = self.l1(x) # 線形回帰
        return x1

変数の定義

# 入力次元数
n_input= x.shape[1]

# 出力次元数
n_output = 1

print(f'入力次元数: {n_input}  出力次元数: {n_output}')
入力次元数: 1  出力次元数: 1

xとyt変数をテンソル化

inputs = torch.tensor(x).float()
labels = torch.tensor(yt).float()

勾配降下法の定義

# 学習率
lr = 0.01

# インスタンス生成 (パラメータ値初期化)
net = Net(n_input, n_output)

# 損失関数: 平均2乗誤差
criterion = nn.MSELoss()

# 最適化関数: 勾配降下法
optimizer = optim.SGD(net.parameters(), lr=lr)

# 繰り返し回数
num_epochs = 50000

# 評価結果記録用 (損失関数値のみ記録)
history = np.zeros((0,2))
# 繰り返し計算メインループ

for epoch in range(num_epochs):
    
    # 勾配値初期化
    optimizer.zero_grad()

    # 予測計算
    outputs = net(inputs)
  
    # 損失計算
    loss = criterion(outputs, labels1) / 2.0

    # 勾配計算
    loss.backward()

    # パラメータ修正
    optimizer.step()

    # 100回ごとに途中経過を記録する
    if ( epoch % 100 == 0):
        history = np.vstack((history, np.array([epoch, loss.item()])))
        print(f'Epoch {epoch} loss: {loss.item():.5f}')
Epoch 0 loss: 154.22493
Epoch 100 loss: 29.61752
Epoch 200 loss: 29.43177
Epoch 300 loss: 29.25043
Epoch 400 loss: 29.07340
Epoch 500 loss: 28.90057
Epoch 600 loss: 28.73186
Epoch 700 loss: 28.56715
Epoch 800 loss: 28.40636
Epoch 900 loss: 28.24939
          ...
Epoch 49600 loss: 21.80033
Epoch 49700 loss: 21.80033
Epoch 49800 loss: 21.80033
Epoch 49900 loss: 21.80033

結果確認

# 損失初期値と最終値
print(f'損失初期値: {history[0,1]:.5f}')
print(f'損失最終値: {history[-1,1]:.5f}')
損失初期値: 154.22493
損失最終値: 21.80033

学習曲線の描画

# 学習曲線の表示 (損失) 
# 最初の1つを除く

plt.plot(history[1:,0], history[1:,1], 'b')
plt.xlabel('繰り返し回数')
plt.ylabel('損失')
plt.title('学習曲線(損失)')
plt.show()

回帰直線の算出

# xの最小値、最大値
xse = np.array((x.min(), x.max())).reshape(-1,1)
Xse = torch.tensor(xse).float()

with torch.no_grad():
  Yse = net(Xse)

print(Yse.numpy())
[[-2.2188644]
 [45.212074 ]]

散布図と回帰直線の描画

plt.scatter(x, yt, s=10, c='b')
plt.xlabel('部屋数')
plt.ylabel('価格')
plt.plot(Xse.data, Yse.data, c='k')
plt.title('散布図と回帰直線')
plt.show()

Discussion