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

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

ただいまの
回答率

88.32%

MobileNet学習中にepoch終了時にValidate関数内で発生したエラーが解決できない。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 353

Otom_jp

score 4

前提・実現したいこと

現在Google ColabにてPyTorchを用いてMobileNet V1を実装。
学習データはImageNetのスモールデータセットであるImageNetteを用いる。
このデータセットは

!wget https://s3.amazonaws.com/fast-ai-imageclas/imagenette-320.tgz
!tar xzf imagenette-320.tgz

コマンドで取得しColab上から対象のGoogleドライブ内のフォルダにアクセスし読み込んでいる。

ベースとなる学習コードはGitHub上の

https://github.com/Z0m6ie/CIFAR-10_PyTorch


を参考にしている。

このデータセットを用いて学習を行いたい。

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

99%まで訓練が終わりvalidationを行う際にエラーが発生している模様。

Requirement already satisfied: tensorboardX in /usr/local/lib/python3.6/dist-packages (2.0)
Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from tensorboardX) (1.12.0)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from tensorboardX) (1.18.4)
Requirement already satisfied: protobuf>=3.8.0 in /usr/local/lib/python3.6/dist-packages (from tensorboardX) (3.10.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.6/dist-packages (from protobuf>=3.8.0->tensorboardX) (47.1.1)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-57-8e253ac717c8> in <module>()
      9 for epoch in range(1):
     10     train(epoch)
---> 11     loss, accuracy = validate(epoch)
     12     best_loss, best_acc = save_best(loss, accuracy, best_loss, best_acc)
     13 

5 frames
/usr/local/lib/python3.6/dist-packages/torchvision/datasets/folder.py in __getitem__(self, index)
    132             tuple: (sample, target) where target is class_index of the target class.
    133         """
--> 134         path, target = self.samples[index]
    135         sample = self.loader(path)
    136         if self.transform is not None:

IndexError: list index out of range

該当のソースコード

以下にノートブック上で学習データの読み込み等を行っている箇所、また、train関数とvalidate関数とそれらの実行を行うソースコードを記す。
なにが原因でこのエラーが起きているのかわからないので原因と解決方法をご教授頂ければ幸いです。

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])

valid_size=0.1

traindir = 'imagenette-320/train'
valdir = 'imagenette-320/val'

train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    normalize
])

valid_transform = transforms.Compose([
         transforms.Resize(256),
         transforms.CenterCrop(224),
         transforms.ToTensor(),
         normalize
])
train_dataset = datasets.ImageFolder(traindir,transform=train_transform)
valid_dataset = datasets.ImageFolder(valdir,transform=valid_transform)

num_train = len(train_dataset)
indices = list(range(num_train))
split = int(np.floor(valid_size * num_train))


np.random.seed(42)
np.random.shuffle(indices)

train_idx, valid_idx = indices[split:], indices[:split]


train_sampler = SubsetRandomSampler(train_idx)
valid_sampler = SubsetRandomSampler(valid_idx)


train_loader = torch.utils.data.DataLoader(train_dataset, 
                batch_size=128, sampler=train_sampler)

valid_loader = torch.utils.data.DataLoader(valid_dataset, 
                batch_size=128, sampler=valid_sampler)
def train(epoch):
    model.train()
    writer = SummaryWriter()
    for batch_idx, (data, target) in enumerate(train_loader):
        if use_cuda:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        optimizer.zero_grad()
        output = model(data)
        correct = 0
        pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability
        #print('pred=',pred)
        correct += pred.eq(target.data.view_as(pred)).sum()
        loss = criterion(output, target)
        loss.backward()
        accuracy = 100 * (int(correct)/ int(len(output)))
        #print('correct=',correct)
        #print('len=',len(output))
        #print(accuracy)
        optimizer.step()
        if batch_idx % 1 == 0:
             #print('correct=',correct)
             #print('len=',len(output))
             #print(accuracy)
             print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}, Accuracy: {:.2f}'.format(epoch, batch_idx * len(data), len(train_loader.dataset),100. * batch_idx / len(train_loader), loss.data.item(), accuracy))
             writer.add_scalar('Loss/Loss', loss.data.item(), epoch)
             writer.add_scalar('Accuracy/Accuracy', accuracy, epoch)
    scheduler.step()
def validate(epoch):
    valid_loss=0.0
    accuracy=0.0
    model.eval()

    writer = SummaryWriter()

    valid_loss = 0
    correct = 0

    for data, target in valid_loader:
        if use_cuda:
            data, target = data.cuda(), target.cuda()

        data, target = Variable(data, volatile=True), Variable(target)
        output = model(data)
        valid_loss += F.cross_entropy(output, target, size_average=False).data.item() # sum up batch loss
        pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability
        correct += pred.eq(target.data.view_as(pred)).sum()


    valid_loss /= len(valid_idx)
    accuracy = 100. * correct / len(valid_idx)
    print('\nValidation set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
        valid_loss, correct, len(valid_idx),
        100. * correct / len(valid_idx)))
    writer.add_scalar('Loss/Validation_Loss', valid_loss, epoch)
    writer.add_scalar('Accuracy/Validation_Accuracy', accuracy, epoch)

    return valid_loss, accuracy
!pip install tensorboardX
from tensorboardX import SummaryWriter
best_loss = None
best_acc = None

for epoch in range(1):
    train(epoch)
    loss, accuracy = validate(epoch)
    best_loss, best_acc = save_best(loss, accuracy, best_loss, best_acc)

試したこと

そもそものデータセットの異常を考えましたが、このデータセットを紹介していた

https://qiita.com/sakaia/items/a8d7b08ebce92da1fc14


こちらのページでは問題なく学習が出来ており、私自身もResNetですが同じ手順を踏み学習できました。
どなたかお力添え頂ければ幸いです

補足情報(FW/ツールのバージョンなど)

PyTorchのバージョンは確認したところ

Name: torch
Version: 1.5.0+cu101
Summary: Tensors and Dynamic neural networks in Python with strong GPU acceleration
Home-page: https://pytorch.org/
Author: PyTorch Team
Author-email: packages@pytorch.org
License: BSD-3
Location: /usr/local/lib/python3.6/dist-packages
Requires: numpy, future
Required-by: torchvision, torchtext, fastai


でした。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

valid_loader = data.DataLoader(valid_dataset, batch_size=128, sampler=valid_sampler)

ここの valid_sampler ですが、

train_idx, valid_idx = indices[split:], indices[:split]
train_sampler = data.SubsetRandomSampler(train_idx)
valid_sampler = data.SubsetRandomSampler(valid_idx)

で train_dataset のインデックスを元に作成したものですよね。
それを valid_dataset を参照する際に使用したら、train_dataset と val_dataset はサンプル数が異なるので、out of range になります。

今回、train と val はディレクトリ構成として別れているので、1つのデータセットを2つに分割するのに使用する sampler を使う必要がそもそもないのではないでしょうか

import torch.utils.data as data
import torchvision.datasets as datasets
import torchvision.transforms as transforms

train_transform = transforms.Compose(
    [
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    ]
)

valid_transform = transforms.Compose(
    [
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    ]
)
train_dataset = datasets.ImageFolder("imagenette-320/train", transform=train_transform)
valid_dataset = datasets.ImageFolder("imagenette-320/val", transform=valid_transform)
train_loader = data.DataLoader(train_dataset, batch_size=128, shuffle=True)
valid_loader = data.DataLoader(valid_dataset, batch_size=128)

追記

Accuracyが100%か0%としか出力されなくなってしました。
これはデータアクセスの方法に問題があるのでしょうか。

データのアクセス方法は問題なさそうに見えます。
100%や0%になるとのことですが、Accuracy や Loss はバッチ単位で表示しても激しく変動するので、あまり参考になりません。1エポックが終了したときにそのエポックの平均を算出して表示するようにしてみてはどうでしょうか。

また、学習が上手くいくかどうかは学習率やバッチサイズなどハイパーパラメータに左右されるので、あるデータセットやモデルで上手くいったものをそのまま別のものに使いまわしても同様に上手くいくとは限らないので、ハイパーパラメータを調整して、上手くいく値を見つける必要があります。

mobilenetv2 (v1 は torchvision になかった) で imagenette-320 を学習するサンプルコードを作成したので、一応載せておきます。

↓ こちらで試したコード
transfer-learning-mobilenetv2-imagenette320.ipynb

参考
Pytorch - 事前学習モデルを使ってクラス分類モデルを学習する方法

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/09 23:37 編集

    dataloader の引数に shuffle=True を入れる必要はありますね。
    これを入れないと、dataloader が返すラベルが 1, 1, ..., 1, 2, 2, ..., 2 のように連番になってしまうので学習に影響がでそうです
    今までは sampler のほうでシャッフルされてたので、問題にならなかったのかも

    キャンセル

  • 2020/06/09 23:52

    ご回答ありがとうございます。
    ご提示いただいた方法で、先の正常に学習が進んでいる状態と同じように学習が進み始めました。仰る通りでした。

    私自身、PyTorchを用いた深層学習に関しては初学者で正直泥沼にハマった感じがありあきらめていたのですが、本当に助かりました。ありがとうございました。

    キャンセル

  • 2020/06/09 23:56

    ディープラーニングは、1つ手順が違うだけで全く上手くいかなくなるということがあるので、ややこしいですよね。解決したようでよかったです。

    キャンセル

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

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る