やりたいこと
犬と猫を見分ける画像分類問題を取り扱うために、pytorchで実装を試みています。
このとき、データセットの作成、モデルの作成、lossの作成から学習部分の実装は大体終わっていて、次にaccuracyを求めたいと考えています。いわゆる精度&推論というところだと思います。
このときのデータセット(256x256x3)として、少ないですが、以下のようになっています。
├─animal_dataset
├─train
│ ├─cat(70枚くらい)
│ └─dog(70枚くらい)
└─val
├─cat(30枚くらい)
└─dog(30枚くらい)
また、lossの収束としては、batchsize=4,epoch=100で以下のようなグラフに収束しました。
本当にやりたいこと
ここからが本当にやりたいことです。
lossの値を出したように精度(accuracy)を確認したいです。
ただ、チュートリアルにあるようなコードを使っても正しいかどうかわからないという点にくろうしています。
そもそも評価ってどうすればいいのか。何をもとに評価しているのか...
そのため、以下に示すコードから間違っている点を指摘していただきたいです。
ソースコード
eval.py(実行コード)
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import model,dataset
from model import *
from tqdm import tqdm
from torch.autograd import Variable
import numpy as np
from matplotlib import pyplot as plt
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
if __name__ == "__main__":
# modelの定義
model = model.Net().to(device)
# 推論モード
model.eval()
# optimizerの定義
optimizer = torch.optim.SGD(
model.parameters(), lr=0.0001, momentum=0.9, weight_decay=0.0001)
#パラメータの読み込み
model.load_state_dict(torch.load("model_cpu.pth", map_location=device))
# validationのデータセット
valid_dataset = dataset.MyDatasets(
root_dir="./animal_dataset",
key="val",
)
valid_loader = torch.utils.data.DataLoader(
valid_dataset,
batch_size=4,
shuffle= True
)
# 評価関数の作成
correct = 0
total = 0
acc = []
with torch.no_grad():
for data in valid_loader:
inputs, labels = data[0].to(device), data[1].to(device)
# print("label={}".format(labels))
# print("inputs={}".format(inputs))
outputs = model(inputs).to(device)
# print(outputs.data)
# 確率
_, predicted = torch.max(outputs.data, 1)
# print(predicted)
total += labels.size(0)
# print(total)
correct += (predicted == labels).sum().item()
# print(correct)
acc.append(float(correct/total))
print("Accuracy of the network on the 100 test images: %d %%" % (100*correct/total))
def plot_acc(acc):
fig, ax = plt.subplots()
epochs = np.arange(1,len(acc)+1)
ax.set_title("accuracy")
ax.plot(epochs, acc)
ax.set_xlabel("Epoch")
print("epochs: {}".format(epochs))
print("acc: {}".format(acc))
plt.savefig("acc.png")
plot_acc(acc)
出力結果
様々な点にprintを入れて出力を見ています。
・精度画像の出力
なかなかにひどいです。(確実に間違っているのですがどこが違うのかわかりません。)
・printの結果
eval.pyの以下にprintを入れた時の出力結果です。
_, predicted = torch.max(outputs.data, 1)
print(predicted)
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([2, 984064])
tensor([0, 0], device='cuda:0')
Accuracy of the network on the 100 test images: 50 %
なんかこの0が並んでるのが違和感です。
追記
プログラムにepochsとaccの内容をprintしました。
.
.
.
torch.Size([2, 984064])
Accuracy of the network on the 100 test images: 50 %
epochs: [1]
acc: [0.5]
このようになっていました。
追記2
lossを収束させたコード(train.py)
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import model,dataset
from model import *
from tqdm import tqdm
from torch.autograd import Variable
import numpy as np
from matplotlib import pyplot as plt
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 上でgpuの設定device
if __name__ == "__main__":
# modelの定義
model = model.Net().to(device)
model.train()
# optimizerの定義
optimizer = torch.optim.SGD(
model.parameters(), lr=0.0001, momentum=0.9, weight_decay=0.0001)
# datasetの定義
# training
train_dataset = dataset.MyDatasets(
root_dir="./animal_dataset",
key="train",
)
train_loader = torch.utils.data.DataLoader(
train_dataset,
batch_size=4,
shuffle= True
)
# validation
valid_dataset = dataset.MyDatasets(
root_dir="./animal_dataset",
key="val",
)
valid_loader = torch.utils.data.DataLoader(
valid_dataset,
batch_size=4,
shuffle= True
)
# batch = len(next(iter(train_loader))) #2
# for i in train_loader:
# print(i)
# for i in valid_loader:
# print(i)
# iterationの確定
sample_size = len(train_dataset) #129
# num_iters = sample_size // 4
#loss
criterion = nn.CrossEntropyLoss().to(device)
losses = []
#start epoch
epoch_num = 100
for epoch in range(epoch_num): # loop over the dataset multiple times
epoch_loss = 0
for i, data in enumerate(train_loader, 0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data[0].to(device), data[1].to(device)
# print("label={}".format(labels))
# print("inputs={}".format(inputs))
outputs = model(inputs).to(device)
# print(model)
# with torch.no_grad():
loss = criterion(outputs, labels)
# loss.requires_grad = True
# print(loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# print(loss)
epoch_loss += loss
losses.append(np.mean(float(epoch_loss)))
print('Finished Training')
"""
層を出力
おそらくtorch.save
torch.loadをつかう??
"""
# for param_tensor in model.state_dict():
print(model.state_dict()['conv1.weight'])
model = model.to('cpu')
torch.save(model.state_dict(), 'model_cpu.pth')
# lossグラフ描画
def plot_history(losses):
fig, ax = plt.subplots()
epochs = np.arange(1, len(losses) + 1)
# 損失の推移
ax.set_title("Loss")
ax.plot(epochs, losses)
ax.set_xlabel("Epoch")
plt.savefig('loss.png')
plot_history(losses)
上記で、lossを収束させています。modelはconv1,conv2の2層です。
ただ、model_cpu.pthの容量が大きすぎて中身見れないみたいです。
追記3
eval.pyとtrain.pyでパラメータの保存とloadを書いた時の出力accを表示します。
epochs: [1]
acc: [0.5740740740740741]
困っていること
以上の結果から正しい精度&推論を出すためのプログラムの書き方を教えてほしいです。
plot_accのグラフを書く関数がおかしいのか。それとももともと評価するところのプログラムがおかしいのかわかりません。どうかご指摘下ればと思います。
また、初心者故にわからないところ、投稿に不備があるかと思います。
その時はご指摘していただけると助かります。
どうか、今回の精度を出す実装をお手伝いくださればと思います。
よろしくお願いいたします。
参考
ciefer10のチュートリアル。精度のほとんどの部分を参考にしました。
torch.maxの使い方について
グラフの描画から精度まで参考にした記事
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
以下をコードに組み込んでください。
学習時に重みを保存するコード
# モデルを保存する。
torch.save(model.state_dict(), "model.pth")
推論時に重みを読み込むコード
# 保存したモデルを読み込む。
model.load_state_dict(torch.load("model.pth"))
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
tiitoi
2020/09/11 01:31
ax.plot(epochs, acc) に与えている
epochs と acc を print() で出力した結果を教えてください
oinari03
2020/09/11 09:39
プログラムにprint()を加え、出力結果を追記しました。
tiitoi
2020/09/11 13:35
そもそも質問のコードは学習じゃなくて全サンプルを推論してその精度を計算しているだけなので、グラフにはならないですよね。
acc: [0.5]
が今のモデルの精度です。
質問のコードには学習したモデルを読み込む処理も見当たらないので、重みはランダムに初期化された状態 (学習前の状態) のままであり、よって当てずっぽうに答えて2クラスで正答率が50%になるというのは妥当な結果だと思います。
oinari03
2020/09/11 14:08 編集
ご指摘ありがとうございます。
精度が0.5というのはなかなか悲しいものなのでもう少し上げた精度が欲しいです。
そのために、モデルのパラメータを入れる必要があるという解釈で大丈夫でしょうか?
投稿の質問タイトルの内容の処理をかいてないのが申し訳ないのですが、
今回投稿した意図は、state_dictでモデルのパラメータをloadすれば推論ができる。と見たが、具体的なやり方がわからない。
いったん、モデルパラメータを加えず、チュートリアル通りに推論の処理を書いた次第です。
例えば、state_dictに関しては、以下のコードで
for param_tensor in model.state_dict():
print(param_tensor, "\t", model.state_dict()[param_tensor].size())
以下の出力が得られました。
```
conv1.weight torch.Size([128, 3, 3, 3])
conv1.bias torch.Size([128])
conv2.weight torch.Size([256, 128, 3, 3])
conv2.bias torch.Size([256])
fc1.weight torch.Size([120, 984064])
fc1.bias torch.Size([120])
fc2.weight torch.Size([84, 120])
fc2.bias torch.Size([84])
fc3.weight torch.Size([2, 84])
fc3.bias torch.Size([2])
```
これはモデルのパラメータを表していることかと思いますが、どのように推論に落とし込むのかに困っています。
結果的に精度向上になるよな改善点があればご指摘願えればと思います。
tiitoi
2020/09/11 14:26
> モデルのパラメータを入れる必要があるという解釈で大丈夫でしょうか?
パラメータを入れるというか、質問のコードはモデルだけ作って学習されていない状態です。
Loss のグラフを作ったということは他のコードで学習したのだと思いますが、だとしたら、そのときに重みを保存して、推論するときはその重みを読み込むという処理が必要です。
質問のコードにはそれが見当たりません。
oinari03
2020/09/11 15:18 編集
根本的にモデルのパラメータをどう処理すればいいかというのが頭に入ってなかったです。
いろいろご指摘いただけたおかげで多分ですが、やっとやろうとしていることを理解しました。
(見当違いだった気がします....)
自分がわからないのはlossを作ったコードから、モデルのパラメータを保存して、今回のeval.pyに引継ぎをする方法ということでした。
失礼かと思いますが、
train.py(lossを収束させたコード)と、自分なりにパラメータを保存するコードをtrain/evalそれぞれに追記いたしましたので、学習後のパラメータを保存して、evalに引き継ぎそれをもとに推論する方法を教えていただけないでしょうか...