3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

pytorchによる回帰の実施

Last updated at Posted at 2021-09-10

##はじめに
以前分類を実施したが、回帰もトライしてみた。
↓前回の記事

##環境
windows 10
python 3.8.10
torch 1.9.0+cu111

##使うデータセット
下記のサイトから。コンクリートの圧縮力について。
説明変数に水の含有力などがある。

##内容
###データセットの準備

import pandas as pd
import numpy as np
import torch
from torch import nn
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torch.utils.data import random_split
from torchvision import transforms
from tqdm import tqdm
import matplotlib.pyplot as plt

#ファイルの読み込みと確認
df = pd.read_csv('Concrete_Data.csv')
df.head()

データはこんな感じ。
image.png

#データを説明変数と目的変数にわける
data = df.drop(df.columns[[-1]],axis=1)
target = df.iloc[:,-1]

# PyTorch で学習に使用できる形式へ変換
data = torch.tensor(data.values, dtype=torch.float32)
target = torch.tensor(target.values, dtype=torch.float32) #今回は回帰なのでfloat32

# 目的変数と入力変数をまとめてdatasetに変換
dataset = torch.utils.data.TensorDataset(data,target)

# 各データセットのサンプル数を決定
# train : val : test = 50% : 20% : 20%
n_train = int(len(dataset) * 0.6)
n_val = int((len(dataset) - n_train) * 0.5)
n_test = len(dataset) - n_train - n_val

# データセットの分割
torch.manual_seed(0) #乱数を与えて固定
train, val, test = torch.utils.data.random_split(dataset, [n_train, n_val,n_test])

#バッチサイズ
batch_size = 32

# 乱数のシードを固定して再現性を確保
torch.manual_seed(0)

# shuffle はデフォルトで False のため、学習データのみ True に指定
train_loader = torch.utils.data.DataLoader(train, batch_size, shuffle=True)
val_loader = torch.utils.data.DataLoader(val, batch_size)
test_loader = torch.utils.data.DataLoader(test, batch_size)

# 辞書型変数にまとめる(trainとvalをまとめて出す)
dataloaders_dict = {"train": train_loader, "val": val_loader}

###ネットワークの定義と学習

class Net(nn.Module):

    # 使用するオブジェクトを定義
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(8, 4)
        self.fc2 = nn.Linear(4, 3)
        self.fc3 = nn.Linear(3, 1)

    # 順伝播
    def forward(self, x):
        
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = x**2*+1
        x = self.fc3(x)
        x = F.relu(x)        
        
        return x    

# インスタンス化
net = Net()

# ネットワークの確認
net

# 損失関数の設定(最小二乗誤差)
criterion = nn.MSELoss()

# 最適化手法の選択
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)

入力変数が8、結合層を上記のように設定した(回帰なので最終的には1つに値が決まる)。2層目のところでReLU関数ではなく、放物線を入れ込んでみた。
損失関数は分類と異なり、最小二乗誤差で定義。

def train_model(net, dataloader_dict, lossfun, optimizer, num_epoch):
    
    l= []
    
    # 重みを保持する変数
    best_acc = 0.0

    # GPUが使えるのであればGPUを有効化する
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    net = net.to(device)
    
    # (エポック)回分のループ
    for epoch in range(num_epoch):

        
        for phase in ['train', 'val']:
            
            if phase == 'train':
                # 学習モード
                net.train()
            else:
                # 推論モード
                net.eval()
                
            epoch_loss = 0.0
            
            # 第1回で作成したDataLoaderを使ってデータを読み込む
            for inputs, labels in tqdm(dataloaders_dict[phase]):
                inputs = inputs.to(device)
                labels = labels.to(device)
                labels = torch.reshape(labels,(-1,1)) #サイズ変更
                # 勾配を初期化する
                optimizer.zero_grad()
                
                # 学習モードの場合のみ勾配の計算を可能にする
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = net(inputs)
                    _, preds = torch.max(outputs, 1)
                    # 損失関数を使って損失を計算する
                    loss = criterion(outputs, labels)
                    
                    if phase == 'train':
                        # 誤差を逆伝搬する
                        loss.backward()
                        # パラメータを更新する
                        optimizer.step()
                        
                    epoch_loss += loss.item() * inputs.size(0)
                    
            # 1エポックでの損失を計算
            epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)
            
            #lossをデータで保存する
            a_loss = np.array(epoch_loss)
            l.append(a_loss)        
        
        #epoch数とlossを表示する
        print('Epoch {}/{}'.format(epoch + 1, num_epoch))   
        print('epoch_loss:{:.4f}'.format(epoch_loss))
        print('-'*20) 
        
        #モデルを保存
        torch.save(net, 'best_model.pth')
                
    #testとvalのlossとaccを抜き出してデータフレーム化
    l_train = l[::2]
    l_train = pd.DataFrame({'train_loss_kaiki':l_train})
            
    l_val = l[1::2]
    l_val = pd.DataFrame({'val_loss_kaiki':l_val})
                                   
    df_loss = pd.concat((l_train,l_val),axis=1)    
    
    #ループ終了後にdfを保存
    df_loss.to_csv('loss_kaiki.csv', encoding='shift_jis')
               
          
#学習と検証
num_epoch = 30
net = train_model(net, dataloaders_dict, criterion, optimizer, num_epoch)

labelを途中reshapeしている。なくてもコードは動くが警告が出る(tensorの型がinputと異なるらしい)ので、一応入れている。

##感想
割と不完全燃焼感が強い:frowning2:
・関数内でlossをプロットしたかったけど、カーネルが死んでしまう(未解決)
・epochの回数を増やすことでlossは減少傾向だけど、結構大きい(もっとepoch増やしたらいい?)
・力尽きたのであとはできる人に投げよう(暴論)。
・とりあえず使ったcsvファイルとlossの結果はgithubに挙げた。
https://github.com/dem-kk/File/tree/main/Concrete

3
1
0

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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?