Hatena::ブログ(Diary)

shi3zの長文日記 RSSフィード Twitter

2016-03-09

[][]とにかくCNNに学習させる大量の画像データが欲しいでごわすという人のためのスクリプト 08:45

 スタンフォード大学のImagenetは、言わずと知れたディープラーニングの画像認識に関わる大会、いわゆるひとつの「ディープラーニングがブレークスルーだー!!」というブームの火付け役になったコンテスト、 Large Scale Visual Recognition Challenge ことILSVRC(もうちょっと気の利いた略称はなかったものか)の主催者である。


 皆が大好きなカリフォルニア大学バークレー校(UCB)が配布しているcaffenetの学習済みデータモデルも、このILSVRCのデータを学習して得られたものである。


 Caltechとかが無料で配布しているCaltech101や256といったデータセットも悪くないが、どうせなら1000クラスとか分類してみたい。みたいでごわす。


 だがしかし!! Imagenetはインターネット上にある画像を膨大なアレしてアレして収集しているため、我々のような企業人はDownloadsに行っても入手することができない!!


 要するに著作権法的にヤバイのでダウンロードできるのは学術関係者に限定される。企業の研究所でもダメっぽい。


 オレも1000クラス分類したい!

 しかしさすがに自分で1000クラスぶん集める根性はない。


 Imagenetはいいぞー。

 現在、1400万枚以上のデータが集まり、2万クラス以上あるらしい。

 特にこのクラスの分類てのがたいへんで、人力がかかる。むしろここに価値がある。


 ILSVRC2012は、ここから指定された1000のオブジェクトカテゴリーで、ランダムに5万枚のデータを学習させる、要するに5000万枚の画像データを学習させて行われた。


 要するに2万クラス全部を覚えさせるというのはいかにも非現実的で、実際2014年に優勝したGoogLeNetだって最後の全結合層の出力は1000しかない。まあここをちょいちょいと書き換えて5000クラス分類にすることはできなくはない。今はTITAN Xがあるしね。


 しかし機械学習系を企業でやってる人が急激に増えてきた(お陰さまでDEEPstation DK-1も売れてます)のだが、学術用データセットのダウンロードが面倒だ。できればいい感じのやつを適当にやりたい。Imagenetから必要なクラス数の画像を必要なだけ持ってきたい。


 そんな打出の小槌のようなスクリプトが、全世界で車輪の再発明をされているのかと思うと目頭に熱いものがこみ上げてきて、そしてまたやるのです。車輪の再発明。でもこの悲劇をここで終わらせなければ!!今でしょ(古い)


 ということで書いたスクリプトを公開しておきます。雑ですけど


 ほんとはimagenetのデータって、ロストしたりしてるんできっちり指定した枚数だけ獲得しようとするとしんどい場合もあります。相手先のデータが死んでたり。一説にはだいたい8割くらいは回収できるとのことなので、倍くらいで指定したらいいかも。


 これを使う前には

$ wget http://image-net.org/archive/words.txt
$ wget http://www.image-net.org/api/text/imagenet.synset.obtain_synset_list

 として、予め語彙とWordnetIDの対応表をダウンロードしておいてください。

 使い方としては

$ python collectImagenet.py --num_of_classes 1000 --num_of_pics 100

 とするとimages以下に1000クラスが100枚ずつ収集されます。

 ツッコまれる前に言っておくと実用性最優先。美しさは二の次です

#
import sys,os
from urllib import urlopen
from urllib import urlretrieve
from urllib2 import URLError,HTTPError 
import commands
import subprocess
import argparse
import random
from PIL import Image
import os.path

def cmd(cmd):
	return commands.getoutput(cmd)

# arguments
parser = argparse.ArgumentParser()
parser.add_argument('--data_dir',        type=str,   default='images')
parser.add_argument('--num_of_classes',  type=int,   default=1000)
parser.add_argument('--num_of_pics',   type=int,   default=10)

args = parser.parse_args()

dict={}
for line in open('words.txt', 'r'):
	line=line.split()
	dict[line[0]]=line[1]

ids = open('imagenet.synset.obtain_synset_list', 'r').read()
ids = ids.split()
random.shuffle(ids)

cmd("mkdir %s"%args.data_dir)
for i in range(args.num_of_classes):
	id = ids[i].rstrip()
	category = dict[id]
	cnt = 0
	if len(category)>0:
		cmd("mkdir %s/%s"%(args.data_dir,category))
		print(category)
		try:
			urls=urlopen("http://www.image-net.org/api/text/imagenet.synset.geturls?wnid="+id).read()
			urls=urls.split()
			random.shuffle(urls)

			j=0
			while cnt<args.num_of_pics if args.num_of_pics<len(urls) else len(urls):
				url = urls[j]
				j+=1
				if j>=len(urls):
					break
				print(url)

				filename = os.path.split(url)[1]
				try:
					output = "%s/%s/%d_%s"%(args.data_dir,category,cnt,filename)
					urlretrieve(url,output )
					try:
						img = Image.open(output)
						size = os.path.getsize(output)
						if size==2051: #flickr Error
							cmd("rm %s"%output)
							cnt-=1							
					except IOError:
						cmd("rm %s"%output)
						cnt-=1
				except HTTPError, e:
					cnt-=1
					print e.reason
				except URLError, e:
					cnt-=1
					print e.reason
				except IOError, e:
					cnt-=1
					print e
				cnt+=1
		except HTTPError, e:
			print e.reason
		except URLError, e:
			print e.reason
		except IOError, e:
			print e



 やったぜバンザイ、と思ったら、たまにリンク先のエラー画像っぽいものを拾ってしまう。

 とりあえずFlickrのエラーだけはみつけて回避してるが泥臭い


 まあでもないよりマシかもしれないしなあ