質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.44%
PyTorch

PyTorchは、オープンソースのPython向けの機械学習ライブラリ。Facebookの人工知能研究グループが開発を主導しています。強力なGPUサポートを備えたテンソル計算、テープベースの自動微分による柔軟なニューラルネットワークの記述が可能です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

629閲覧

Pytorch neural networkで学習曲線が直線になる原因

kak

総合スコア27

PyTorch

PyTorchは、オープンソースのPython向けの機械学習ライブラリ。Facebookの人工知能研究グループが開発を主導しています。強力なGPUサポートを備えたテンソル計算、テープベースの自動微分による柔軟なニューラルネットワークの記述が可能です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/04/17 02:39

編集2023/04/17 04:05

実現したいこと

Pytorchのneural networkを実装したところ、学習曲線が直線的になる原因が知りたいです。

前提

現在、Python3.7にPytorchを入れて、
https://free.kikagaku.ai/tutorial/basic_of_deep_learning/learn/pytorch_basic
こちらのサイトを参考に、92のパラメータから3つのクラスに分類する2層のneural networkを作成しました。

発生している問題・エラーメッセージ

学習結果は98%程度得られていいのですが、学習曲線を描出すると、直線的になります。
更に検証用、テスト用のデータセットをランしても同様に直線的な結果になります。
バッチサイズも10にしていますが、x軸のサンプルサイズが3,000近くになります。

エラーメッセージ

イメージ説明

該当のソースコード

import pandas as pd import numpy as np import torch import torch.nn as nn import torch.nn.functional as F data = pd.read_csv("data.csv", index_col = 0) label=pd.read_csv("label.csv",index_col = 0) data=data.astype('f') label=label.values.astype('i') data=np.array(data,dtype=np.float32) label=label.reshape(-1) x = torch.tensor(data, dtype=torch.float32) t = torch.tensor(label, dtype=torch.int64) print(x.size()) print(t.size()) dataset = torch.utils.data.TensorDataset(x, t) # 各データセットのサンプル数を決定 # train : val: test = 60% : 20% : 20% n_train = int(len(dataset) * 0.6) n_val = int(len(dataset) * 0.2) 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 = 10 # 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) class Net(nn.Module): # 使用するオブジェクトを定義 def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(92, 31) self.fc2 = nn.Linear(31, 3) # 順伝播 def forward(self, x): x = self.fc1(x) x = F.relu(x) x = self.fc2(x) return x # 乱数のシードを固定して再現性を確保 torch.manual_seed(0) # インスタンス化 net = Net() criterion = F.cross_entropy optimizer = torch.optim.SGD(net.parameters(), lr=0.1) batch = next(iter(train_loader)) x, t = batch # 予測値の算出 y = net.forward(x) # call メソッドを用いた forward の計算(推奨) y = net(x) # 目的関数の計算 criterion の call メソッドを利用 loss = criterion(y, t) # 全結合層 fc1 の重みに関する勾配 net.fc1.weight.grad # 全結合層 fc1 のバイアスに関する勾配 net.fc1.bias.grad # 全結合層 fc2 の重みに関する勾配 net.fc2.weight.grad # 全結合層 fc2 のバイアスに関する勾配 net.fc2.bias.grad # 勾配の算出 loss.backward() # 勾配の情報を用いたパラメータの更新 optimizer.step() # 演算に使用できる GPU の有無を確認 torch.cuda.is_available() # GPU の設定状況に基づいたデバイスの選択 device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 指定したデバイスへのモデルの転送 net.to(device) # 指定したデバイスへの入力変数の転送 x = x.to(device) # 指定したデバイスへの目的変数の転送 t = t.to(device) # 勾配情報の初期化 optimizer.zero_grad() # エポックの数 max_epoch = 1 ############################################################# # モデルの初期化 torch.manual_seed(0) # モデルのインスタンス化とデバイスへの転送 net = Net().to(device) # 最適化手法の選択 optimizer = torch.optim.SGD(net.parameters(), lr=0.1) # 学習ループ for epoch in range(max_epoch): for batch in train_loader: # バッチサイズ分のサンプルを抽出 x, t = batch # 学習時に使用するデバイスへデータの転送 x = x.to(device) t = t.to(device) # パラメータの勾配を初期化 optimizer.zero_grad() # 予測値の算出 y = net(x) # 目標値と予測値から目的関数の値を算出 loss = criterion(y, t) # 目的関数の値を表示して確認 # item(): tensot.Tensor => float print('loss: ', loss.item()) # 各パラメータの勾配を算出 loss.backward() # 勾配の情報を用いたパラメータの更新 optimizer.step() # dim=1 で行ごとの最大値に対する要素番号を取得(dim=0 は列ごと) y_label = torch.argmax(y, dim=1) # 予測値から最大となるクラスの番号を取り出した結果 y_label # 正解率 acc = torch.sum(y_label == t) * 1.0 / len(t) acc ####################################################### # モデルの初期化 torch.manual_seed(0) # モデルのインスタンス化とデバイスへの転送 net = Net().to(device) # 最適化手法の選択 optimizer = torch.optim.SGD(net.parameters(), lr=0.1) for epoch in range(max_epoch): for batch in train_loader: x, t = batch x = x.to(device) t = t.to(device) optimizer.zero_grad() y = net(x) loss = criterion(y, t) # New:正解率の算出 y_label = torch.argmax(y, dim=1) acc = torch.sum(y_label == t) * 1.0 / len(t) print('accuracy:', acc) loss.backward() optimizer.step() val_loss_list = [] val_acc_list = [] # 正解率の計算 def calc_acc(data_loader): with torch.no_grad(): for epoch in range(max_epoch): accs = [] # 各バッチごとの結果格納用 val_loss = 0 val_acc = 0 for i, batch in enumerate(data_loader): x, t = batch x = x.to(device) t = t.to(device) y = net(x) loss = criterion(y, t) val_loss += loss.item() val_acc += (y.max(1)[1] == t).sum().item() #y_label = torch.argmax(y, dim=1) #acc = torch.sum(y_label == t) * 1.0 / len(t) #accs.append(acc) avg_val_loss = val_loss / len(data_loader.dataset) avg_val_acc = val_acc / len(data_loader.dataset) print ('Epoch [{}/{}], val_loss: {val_loss:.4f}, val_acc: {val_acc:.4f}' .format(epoch+1, max_epoch, i+1, val_loss=avg_val_loss, val_acc=avg_val_acc)) val_loss_list.append(avg_val_loss) val_acc_list.append(avg_val_acc) # 全体の平均を算出 # avg_acc = torch.tensor(accs).mean() #print('Accuracy: {:.1f}%'.format(avg_acc * 100)) #return avg_acc calc_acc(val_loader) #正解率の書き出し pd.DataFrame(val_acc_list).to_csv("val_acc.csv")

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

PondVillege

2023/04/17 03:23

タグについているNeural Network ConcoleはSONYのMLツールなので,本質問に関係のないタグとなっていますので削除の検討をお願いします.同時に,貴コードはPythonで書かれたものなのでPythonタグを付けると閲覧数が増えて回答が得られやすくなります. また,学習曲線を描画するコードを掲載いただけると原因の解明に繋がりそうなので,追記願います.
kak

2023/04/17 04:07

コメントありがとうございます。 タグを編集いたしました。 学習曲線は #正解率の書き出し pd.DataFrame(val_acc_list).to_csv("val_acc.csv") で得たCSVから単純にエクセルで描画致しました。 よろしくお願いいたします。
PondVillege

2023/04/17 06:19

ひとまず改善案は置いといて,学習曲線が直線的になる原因ですが, calc_acc内のfor i, batch in enumerate(data_loader):のループで val_acc += (y.max(1)[1] == t).sum().item() avg_val_acc = val_acc / len(data_loader.dataset) val_acc_list.append(avg_val_acc) としていることが原因です.1バッチごとにデータ全体観点の正解率を出してそれを累積していっているので,一番最後val_acc_list[-1]がそのエポックでの正解率になっています.
kak

2023/04/17 09:05

ありがとうございます! #y_label = torch.argmax(y, dim=1) #acc = torch.sum(y_label == t) * 1.0 / len(t) #accs.append(acc) の部分からacc.sum().item()*100でバッチ毎の正解が取り出せました。 是非、同じ内容で構いませんので、回答欄にコメントをコピペして頂ければ幸いです。
guest

回答1

0

ベストアンサー

学習曲線が直線的になる原因ですが,calc_acc内のfor i, batch in enumerate(data_loader):のループで

Python

1val_acc += (y.max(1)[1] == t).sum().item() 2avg_val_acc = val_acc / len(data_loader.dataset) 3val_acc_list.append(avg_val_acc)

としていることが原因です.1バッチごとにデータ全体観点の正解率を出してそれを累積していっているので,一番最後val_acc_list[-1]がそのエポックでの正解率になっています.

普通,学習曲線を描くときの検証ループはoptimizer.step()の後に検証用データで1エポック分回して損失や精度を算出します.(現状のコードは学習完了してから検証をしています.学習完了してから使うデータはテストデータの方かと思います.)

投稿2023/04/17 09:38

PondVillege

総合スコア1579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kak

2023/04/17 23:59

ご丁寧なご回答ありがとうございました! 動かすのに必死だったので、検証用、テスト用の意味があるのかと思いながら、そのままにしていました。 今後ブラッシュアップしていきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問