pythonを使って簡単な画像分類を実現する - s.t.Mindで2クラスの画像分類を試しましたが、今回は多クラスの画像分類です。
多クラスの画像分類
多クラスになっても画像分類のステップ自体は2クラスの時と同じです。
- 学習画像を集める
- 画像を特徴量に変換する
- 識別器を学習, モデルを評価する
学習画像の取得
今回の学習画像もBingから集めてきました。下のスクリプトを使って、architecture、car、dog、food、peopleの5種類で、それぞれ35枚の画像セットです。
#-*- coding: utf-8 -*- from __future__ import with_statement from bs4 import BeautifulSoup import requests import re import urllib2 import os import argparse def get_soup(url): return BeautifulSoup(requests.get(url).text) def get_images_from_bing(query): url = "http://www.bing.com/images/search?q=" + query + \ "&gft=+filterui:color2-bw+filterui:imagesize-large&FORM=R5IR3" soup = get_soup(url) images = [a['src2'] for a in soup.find_all('img', {'src2': re.compile('mm.bing.net')})] for img in images: raw_img = urllib2.urlopen(img).read() cntr = len([i for i in os.listdir('images') if query in i]) + 1 with open('images/' + query + '_' + str(cntr) + '.jpg', 'w') as f: f.write(raw_img) return cntr def main(): parser = argparse.ArgumentParser( description='collect training images from Bing' ) parser.add_argument( dest='query', help='search query', nargs='+', ) args = parser.parse_args() for q in args.query: num_images = get_images_from_bing(q) print '[%s] got %d images' % (q, num_images) if __name__ == '__main__': main()
$ python collect_images.py architecture food car dog people [architecture] got 35 images [food] got 35 images [car] got 35 images [dog] got 35 images [people] got 35 images
特徴量の取得と識別器学習
特徴量は前回と同様で、画素のRGBをそのまま使う方式です。識別器の学習も基本的には同じですが、前回はarchitecture(0)とfood(1)の2種類のラベルが、今回はarchitecture(0), car(1), dog(2), food(3), people(4)の5種類になります。
多クラスの識別器の学習には、OneVsRestClassifierを使います。ただし、scikit-learnで提供されるSVCやLinearSVCはそもそも多クラスに対応しているので、OneVsRestClassifierを使わなくても多クラス識別器の学習が出来ます。参照: 1.10. Multiclass and multilabel algorithms — scikit-learn 0.14 documentation
#-*- coding: utf-8 -*- import os import numpy as np from PIL import Image from sklearn.multiclass import OneVsRestClassifier from sklearn.decomposition import RandomizedPCA from sklearn.svm import LinearSVC import pandas as pd STANDARD_SIZE = (300, 167) def img_to_matrix(filename, verbose=False): """ Load image as array Returns ------- imgArray : numpy array Image is resized to STANDARD_SIZE """ img = Image.open(filename) if verbose: print 'changing size from %s to %s' % (str(img.size), str(STANDARD_SIZE)) img = img.resize(STANDARD_SIZE) imgArray = np.asarray(img) return imgArray def flatten_image(img): """ Flatten image array Parameters ---------- img : numpy array Image array Returns ------- img_wide : numpy array """ img_wide = img.reshape(1, img.size) return img_wide[0] def main(): img_dir = 'images/' images = [img_dir + f for f in os.listdir(img_dir)] labels = [f.split('/')[-1].split('_')[0] for f in images] label2ids = {v: i for i, v in enumerate(sorted(set(labels), key=labels.index))} y = np.array([label2ids[l] for l in labels]) data = [] for image_file in images: img = img_to_matrix(image_file) img = flatten_image(img) data.append(img) data = np.array(data) # training samples is_train = np.random.uniform(0, 1, len(data)) <= 0.7 train_X, train_y = data[is_train], y[is_train] # training a classifier pca = RandomizedPCA(n_components=5) train_X = pca.fit_transform(train_X) multi_svm = OneVsRestClassifier(LinearSVC()) multi_svm.fit(train_X, train_y) # evaluating the model test_X, test_y = data[is_train == False], y[is_train == False] test_X = pca.transform(test_X) print pd.crosstab(test_y, multi_svm.predict(test_X), rownames=['Actual'], colnames=['Predicted']) if __name__ == '__main__': main()
結果
今回の結果はいまいちでした。
$ python train_model.py Predicted 0 1 2 3 4 Actual 0 6 0 2 0 1 1 2 1 0 3 2 2 0 0 0 3 1 3 0 2 2 7 1 4 1 0 0 4 9
高々数十枚の学習画像で、しかも単に画像全体を特徴として使うという単純な方法では、クラス数が増えるとやはり難しいですね。 今回の結果はさておき、scikit-learnを使うと簡単に多クラス分類の実装ができるので、色々と試して見ると面白いかなと思います。