前提
chainerで画像分類の実験を行っています。学習させたモデルでテストを行うと0.1%しか出ないという状態です。
画像のデータセットについては特に問題ないと考えています。
実現したいこと
- 問題の解決
該当のソースコード
python
1テスト用のコード 2 3#!/usr/bin/env python 4"""Example code of learning a large scale convnet from ILSVRC2012 dataset. 5 6Prerequisite: To run this example, crop the center of ILSVRC2012 training and 7validation images and scale them to 256x256, and make two lists of space- 8separated CSV whose first column is full path to image and second column is 9zero-origin label (this format is same as that used by Caffe's ImageDataLayer). 10 11""" 12from __future__ import print_function 13import argparse 14import datetime 15import json 16import multiprocessing 17import random 18import sys 19import threading 20import time 21 22import numpy as np 23from PIL import Image 24 25 26import six 27import six.moves.cPickle as pickle 28import _pickle as cPickle #変更 29from six.moves import queue 30 31import chainer 32import matplotlib.pyplot as plt 33import numpy as np 34import math 35import chainer.functions as F 36import chainer.links as L 37from chainer.links import caffe 38from matplotlib.ticker import * 39from chainer import serializers 40 41from functools import cmp_to_key #追加 42 43 44 45parser = argparse.ArgumentParser( 46 description='Image inspection using chainer') 47parser.add_argument('image', help='Path to inspection image file') 48parser.add_argument('--model','-m',default='model', help='Path to model file') 49parser.add_argument('--mean', default='mean.npy', 50 help='Path to the mean file (computed by compute_mean.py)') 51parser.add_argument('--out', '-o') 52args = parser.parse_args() 53 54 55def read_image(path, center=False, flip=False): 56 image = np.asarray(Image.open(path)).transpose(2, 0, 1) 57 if center: 58 top = left = cropwidth // 2 59 else: 60 top = random.randint(0, cropwidth - 1) 61 left = random.randint(0, cropwidth - 1) 62 bottom = model.insize + top 63 right = model.insize + left 64 image = image[:, top:bottom, left:right].astype(np.float32) 65 image -= mean_image[:, top:bottom, left:right] 66 image /= 255 67 if flip and random.randint(0, 1) == 0: 68 return image[:, :, ::-1] 69 else: 70 return image 71 72import nin 73 74mean_image = cPickle.load(open(args.mean, 'rb')) 75 76model = cPickle.load(open(args.model,'rb')) 77 78#mean_image = cPickle.load(open(args.mean, 'rb')) 79#model = cPickle.load(open(args.model,'rb',)) 80 81#serializers.load_hdf5("gpu1out.h5", model) 82cropwidth = 256 - model.insize 83model.to_cpu() 84 85 86def predict(net, x): 87 h = F.max_pooling_2d(F.relu(net.mlpconv1(x)), 3, stride=2) 88 h = F.max_pooling_2d(F.relu(net.mlpconv2(h)), 3, stride=2) 89 h = F.max_pooling_2d(F.relu(net.mlpconv3(h)), 3, stride=2) 90 #h = net.mlpconv4(F.dropout(h, train=net.train)) 91 h = net.mlpconv4(F.dropout(h)) 92 h = F.reshape(F.average_pooling_2d(h, 6), (x.data.shape[0], 1000)) 93 return F.softmax(h) 94 95#setattr(model, 'predict', predict) 96 97img = read_image(args.image) 98x = np.ndarray( 99 (1, 3, model.insize, model.insize), dtype=np.float32) 100x[0]=img 101#x = chainer.Variable(np.asarray(x), volatile='on') 102x= chainer.Variable(np.asarray(x)) 103with chainer.no_backprop_mode(): 104 105 score = predict(model,x) 106#score=cuda.to_cpu(score.data) 107 108categories = np.loadtxt("labels.txt", str, delimiter="\t") 109 110top_k = 20 111#↓list()を追加 112prediction = list(zip(score.data[0].tolist(), categories)) 113#prediction.sort(cmp=lambda x, y: cmp(x[0], y[0]), reverse=True) 114prediction.sort (key=lambda x: x[0],reverse=True) 115 116for rank, (score, name) in enumerate(prediction[:top_k], start=1): 117 print('#%d | %s | %4.1f%%' % (rank, name, score * 100)) 118 119 120今回使用した nin.py 121import math 122import chainer 123import chainer.functions as F 124import chainer.links as L 125 126 127class NIN(chainer.Chain): 128 129 """Network-in-Network example model.""" 130 131 insize = 227 132 133 def __init__(self): 134 w = math.sqrt(2) # MSRA scaling 135 super(NIN, self).__init__( 136 mlpconv1=L.MLPConvolution2D( 137 3, (96, 96, 96), 11, stride=4), 138 mlpconv2=L.MLPConvolution2D( 139 96, (256, 256, 256), 5, pad=2), #wscale=w削除 140 mlpconv3=L.MLPConvolution2D( 141 256, (384, 384, 384), 3, pad=1), 142 mlpconv4=L.MLPConvolution2D( 143 384, (1024, 1024, 1000), 3, pad=2), 144 ) 145 self.train = True 146 147 def clear(self): 148 self.loss = None 149 self.accuracy = None 150 151 def __call__(self, x, t): 152 self.clear() 153 h = F.max_pooling_2d(F.relu(self.mlpconv1(x)), 3, stride=2) 154 h = F.max_pooling_2d(F.relu(self.mlpconv2(h)), 3, stride=2) 155 h = F.max_pooling_2d(F.relu(self.mlpconv3(h)), 3, stride=2) 156 h = self.mlpconv4(F.dropout(h)) #変更 157 h = F.reshape(F.average_pooling_2d(h, 6), (x.data.shape[0], 1000)) 158 159 self.loss = F.softmax_cross_entropy(h, t) 160 self.accuracy = F.accuracy(h, t) 161 return self.loss 162 163 def predict(self, x_data, train=False): 164 x = chainer.Variable(x_data, volatile=True) 165 166 h = F.relu(self.conv1(x)) 167 h = F.relu(self.conv1a(h)) 168 h = F.relu(self.conv1b(h)) 169 h = F.max_pooling_2d(h, 3, stride=2) 170 h = F.relu(self.conv2(h)) 171 h = F.relu(self.conv2a(h)) 172 h = F.relu(self.conv2b(h)) 173 h = F.max_pooling_2d(h, 3, stride=2) 174 h = F.relu(self.conv3(h)) 175 h = F.relu(self.conv3a(h)) 176 h = F.relu(self.conv3b(h)) 177 h = F.max_pooling_2d(h, 3, stride=2) 178 h = F.dropout(h, train=train) 179 h = F.relu(self.conv4(h)) 180 h = F.relu(self.conv4a(h)) 181 h = F.relu(self.conv4b(h)) 182 h = F.reshape(F.average_pooling_2d(h, 6), (x_data.shape[0], 1000)) 183 return F.softmax(h) 184 185学習用のコード 186def train_loop(): 187 # Trainer 188 graph_generated = False 189 while True: 190 while data_q.empty(): 191 time.sleep(0.1) 192 inp = data_q.get() 193 if inp == 'end': # quit 194 res_q.put('end') 195 break 196 elif inp == 'train': # restart training 197 res_q.put('train') 198 model.train = True 199 continue 200 elif inp == 'val': # start validation 201 res_q.put('val') 202 #serializers.save_npz(args.out, model) 203 #model.to_cpu() 204 pickle.dump(model, open(args.out, 'wb'), -1) 205 serializers.save_npz(args.outstate, optimizer) 206 model.train = False 207 continue 208 209 volatile = 'off'if model.train else 'on' 210 #x = chainer.Variable(xp.asarray(inp[0]), volatile=volatile) 211 x = chainer.Variable(xp.asarray(inp[0])) 212 with chainer.no_backprop_mode(): 213 #t = chainer.Variable(xp.asarray(inp[1]),volatile=volatile) 214 t = chainer.Variable(xp.asarray(inp[1])) 215 with chainer.no_backprop_mode(): 216 #if model train: 217 if model: 218 optimizer.update(model, x, t) 219 if not graph_generated: 220 with open('graph.dot', 'w') as o: 221 o.write(computational_graph.build_computational_graph( 222 (model.loss,)).dump()) 223 print('generated graph', file=sys.stderr) 224 graph_generated = True 225 else: 226 model(x, t) 227 res_q.put((float(model.loss.data), float(model.accuracy.data))) 228 del x, t 229 230data_qにデータを入れる 231 232 def feed_data(): 233 # Data feeder 234 i = 0 235 count = 0 236 237 x_batch = np.ndarray( 238 (args.batchsize, 3, model.insize, model.insize), dtype=np.float32) 239 y_batch = np.ndarray((args.batchsize,), dtype=np.int32) 240 val_x_batch = np.ndarray( 241 (args.val_batchsize, 3, model.insize, model.insize), dtype=np.float32) 242 val_y_batch = np.ndarray((args.val_batchsize,), dtype=np.int32) 243 244 batch_pool = [None] * args.batchsize 245 val_batch_pool = [None] * args.val_batchsize 246 pool = multiprocessing.Pool(args.loaderjob) 247 data_q.put('train') 248 for epoch in six.moves.range(1, 1 + args.epoch): 249 print('epoch', epoch, file=sys.stderr) 250 print('learning rate', optimizer.lr, file=sys.stderr) 251 perm = np.random.permutation(len(train_list)) 252 for idx in perm: 253 path, label = train_list[idx] 254 batch_pool[i] = pool.apply_async(read_image, (path, False, True)) 255 y_batch[i] = label 256 i += 1 257 258 if i == args.batchsize: 259 for j, x in enumerate(batch_pool): 260 x_batch[j] = x.get() 261 data_q.put((x_batch.copy(), y_batch.copy())) 262 i = 0 263 264 count += 1 265 if count % denominator == 0: 266 data_q.put('val') 267 j = 0 268 for path, label in val_list: 269 val_batch_pool[j] = pool.apply_async( 270 read_image, (path, True, False)) 271 val_y_batch[j] = label 272 j += 1 273 274 if j == args.val_batchsize: 275 for k, x in enumerate(val_batch_pool): 276 val_x_batch[k] = x.get() 277 data_q.put((val_x_batch.copy(), val_y_batch.copy())) 278 j = 0 279 data_q.put('train') 280 281 optimizer.lr *= 0.97 282 pool.close() 283 pool.join() 284 data_q.put('end') 285### 試したこと 286学習させる画像の枚数や学習回数を変更した。
画像サイズとConfusion Matrixを示していただけると原因究明に役立ちそうです
画像サイズは256×256です。
confusion matrixを追加していませんが他の関数を使っているということでしょうか。
勉強不足で申し訳ないです。
Confusion Matrixとは,分類問題において精度や再現率などを評価するための表です.日本語では混同行列とも呼ばれます.
https://www.google.com/search?q=confusion+matrix
今回使われたデータセットILSVRC2012では1000カテゴリあるとのことで,かつモデルの精度が0.1%なんですよね,この0.1%というのは,ある入力画像に対して1000カテゴリからランダムに何か1つ選ぶときの精度と同じです.つまるところ,このモデルは何も学習していない可能性があります.
ただ,精度だけでは評価しづらいものがあるので,Confusion Matrixによる評価を経て,モデルが完全にRandom Choiceを行うものになっていることを証明した上で,コードやモデルに問題が無いか探そう.というものです.
ちなみにモデルもnet.mlpconv1() -> ReLU() -> MaxPooling2d()の繰り返しであることはわかるのですが,これら畳み込み層のカーネル数などのハイパーパラメータがモデルの性能に影響するので,明示していただきたいところです.
色々とご説明ありがとうございます。今回学習のために使用したコードを↑に追加しました。
学習させるためのコードです。↓
よろしくお願いします。
この欄では文頭の半角スペースが消されるので,こちらのコードも質問を編集して書き込んでいただけるとありがたいです.
optimizer.update(model, x, t)
の直下に
print(f"acc: {model.accuracy.data}, loss: {model.loss.data}")
として表示させみてください.各epochでの数値の遷移を見てみたいです.微塵も動かなければ学習時のコードがおかしいです.
多分,with chainer.no_backprop_mode():の中でoptimizer.update()しているのがダメだと思います.
実行してみたところ、lossの数値に変化はあったのですが、accの方が0.0のままでした。
train_roopのコードを変える必要があるということでしょうか。
> 実行してみたところ、lossの数値に変化はあったのですが、accの方が0.0のままでした。
dropoutがあるので,そのレイヤでの重みの組み合わせで,当然lossの数値に変化が出ます.
私が見たい遷移は,中長期的にどのような増減をしたか,というものになります.
横ばいである場合,学習が進んでいませんし,少しずつ減少するなら少なくとも学習は進んでいる証拠になります.
> train_roopのコードを変える必要があるということでしょうか。
多分そうです,学習時のコードの全体像がまだわからなくて,具体的にどこに原因があるかわからないといった状態です.並列処理でdata_qにクエリを投げている.という理解でよろしいですか?どのようにクエリを投げていますか?
>私が見たい遷移は,中長期的にどのような増減をしたか,というものになります.
lossのはずっと6.9辺りを動いている感じです。
>どのようにクエリを投げていますか?
data_qに関するコードを一応↑に追加しました。train_listからnp.random.permutation()を用いてデータをシャッフルして渡しています。
上の場合はできませんでした。
また、volatileが削除されてしまったためwith chainer.no_backprop_mode()を追加したのですが、そういった場合どうすればよいでしょうか。
速度に差があるだけなので,とりあえずvolatileなしにするだけで実行してみましょうか
with chainer.no_backprop_mode()を追加されていますが,このなかでVariableを実行すると必ずvolatile=Trueにしたことと同じようになってしまうので,chainer.using_config()で代用してvolatile=Falseの場合の処理も書かなくてはなりません.
https://docs.chainer.org/en/latest/upgrade_v2.html#volatile-flag-is-removed
の「Volatile flag is removed」の「Example」を見ると、Chainer v1用のコードで「volatile=True」だった場合は、Chainer v2では「with chainer.no_backprop_mode():」を使うように書いてあります
この質問のコードでは、
> volatile = 'off'if model.train else 'on'
から、「model.trainではない」場合が「volatile=on」(=True)になるので、ps_aux_grepさんの回答のように、「model.trainではない」場合のみ「with chainer.no_backprop_mode():」を使います
お二方多くの解決方法を提示してくださってありがとうございました。

回答1件
あなたの回答
tips
プレビュー