OpenCVアルゴリズムの検証
前書き
まず、返信に時間がかかって申し訳ありませんでしたが、時間に余裕がありませんでした。実際にアルゴリズムを検証することは非常に興味深いトピックであり、それほど難しいことではありません。この投稿では、アルゴリズムを検証する方法を示します(FaceRecognizerを要求したので、それを使用します)。いつものように、私の投稿では、完全なソースコードの例を示します。これは、コードで説明する方がはるかに簡単だと思うからです。
ですから、人々が「私のアルゴリズムのパフォーマンスが悪い」と言うときはいつでも、私は彼らに尋ねます:
- 実際に何が悪いのですか?
- 1つのサンプルを見て、これを評価しましたか?
- あなたの画像データは何でしたか?
- トレーニングデータとテストデータをどのように分割しますか?
- あなたの測定基準は何ですか?
- [...]
私の希望は、この投稿が混乱を解消し、アルゴリズムの検証がいかに簡単であるかを示すことです。コンピュータービジョンと機械学習アルゴリズムの実験から学んだことは次のとおりです。
- 適切な検証がなければ、それはすべて幽霊を追いかけることです。あなたは本当に、本当に話すために数字が必要です。
この投稿のすべてのコードはBSDライセンスの下に置かれているので、プロジェクトに自由に使用してください。
アルゴリズムの検証
コンピュータビジョンプロジェクトの最も重要なタスクの1つは、画像データを取得することです。本番環境で期待するものと同じ画像データを取得する必要があるため、ライブ配信時に悪い経験をすることはありません。非常に実用的な例:野生の顔を認識したい場合、非常に制御されたシナリオで撮影された画像でアルゴリズムを検証することは役に立ちません。データは王様なので、できるだけ多くのデータを取得してください。それはデータです。
いくつかのデータを取得し、アルゴリズムを記述したら、それを評価することになります。検証にはいくつかの戦略がありますが、単純な相互検証から始めて、そこから先に進む必要があると思います。相互検証の詳細については、以下を参照してください。
すべてを自分で実装する代わりに、scikit-learnを利用して優れたオープンソースプロジェクトを作成します。
アルゴリズムを検証するための非常に優れたドキュメントとチュートリアルがあります。
したがって、計画は次のとおりです。
- 一部の画像データを読み取る関数を記述します。
cv2.FaceRecognizer
をscikit-learn推定量にラップします。
cv2.FaceRecognizer
与えられた検証と測定基準を使用して、パフォーマンスを見積もります。
- 利益!
画像データを正しく取得する
まず、読む画像データにいくつかの単語を書きたいと思います。これに関する質問はほとんどの場合ポップアップするからです。簡単にするために、例では画像(顔、認識したい人)がフォルダに入れられていると仮定しました。1人につき1つのフォルダ。images
したがって、サブフォルダーperson1
などを含むフォルダー(データセット)呼び出しがあると想像してくださいperson2
。
philipp@mango:~/facerec/data/images$ tree -L 2 | head -n 20
.
|-- person1
| |-- 1.jpg
| |-- 2.jpg
| |-- 3.jpg
| |-- 4.jpg
|-- person2
| |-- 1.jpg
| |-- 2.jpg
| |-- 3.jpg
| |-- 4.jpg
[...]
このようなフォルダ構造ですでに提供されている公開されているデータセットの1つは、AT&TFacedatabaseです。
解凍すると、次のようになります(私のファイルシステムでは/home/philipp/facerec/data/at/
、解凍先はパスが異なります!):
philipp@mango:~/facerec/data/at$ tree .
.
|-- README
|-- s1
| |-- 1.pgm
| |-- 2.pgm
[...]
| `-- 10.pgm
|-- s2
| |-- 1.pgm
| |-- 2.pgm
[...]
| `-- 10.pgm
|-- s3
| |-- 1.pgm
| |-- 2.pgm
[...]
| `-- 10.pgm
...
40 directories, 401 files
それを一緒に入れて
そこでまずread_images
、画像データとラベルを読み込む方法を定義します。
import os
import sys
import cv2
import numpy as np
def read_images(path, sz=None):
"""Reads the images in a given folder, resizes images on the fly if size is given.
Args:
path: Path to a folder with subfolders representing the subjects (persons).
sz: A tuple with the size Resizes
Returns:
A list [X,y]
X: The images, which is a Python list of numpy arrays.
y: The corresponding labels (the unique number of the subject, person) in a Python list.
"""
c = 0
X,y = [], []
for dirname, dirnames, filenames in os.walk(path):
for subdirname in dirnames:
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
try:
im = cv2.imread(os.path.join(subject_path, filename), cv2.IMREAD_GRAYSCALE)
# resize to given size (if given)
if (sz is not None):
im = cv2.resize(im, sz)
X.append(np.asarray(im, dtype=np.uint8))
y.append(c)
except IOError, (errno, strerror):
print "I/O error({0}): {1}".format(errno, strerror)
except:
print "Unexpected error:", sys.exc_info()[0]
raise
c = c+1
return [X,y]
画像データの読み込みは、呼び出すのと同じくらい簡単になります。
[X,y] = read_images("/path/to/some/folder")
一部のアルゴリズム(固有顔、フィッシャーフェイスなど)では、画像のサイズを同じにする必要があるため、2番目のパラメーターを追加しましたsz
。タプルを渡すことによりsz
、すべての画像のサイズが変更されます。したがって、次の呼び出しはすべての画像のサイズ/path/to/some/folder
を100x100
ピクセルに変更します。
[X,y] = read_images("/path/to/some/folder", (100,100))
scikit-learnのすべての分類子は、とメソッドBaseEstimator
を持つことになっている、から派生しています。このメソッドはサンプルと対応するラベルのリストを取得するため、のtrainメソッドにマップするのは簡単です。このメソッドはサンプルと対応するラベルのリストも取得しますが、今回は各サンプルの予測を返す必要があります。fit
predict
fit
X
y
cv2.FaceRecognizer
predict
from sklearn.base import BaseEstimator
class FaceRecognizerModel(BaseEstimator):
def __init__(self):
self.model = cv2.createEigenFaceRecognizer()
def fit(self, X, y):
self.model.train(X,y)
def predict(self, T):
return [self.model.predict(T[i]) for i in range(0, T.shape[0])]
次に、テストするさまざまな検証方法とメトリックから選択できますcv2.FaceRecognizer
。利用可能な相互検証アルゴリズムはsklearn.cross_validationにあります。
- Leave-One-Out相互検証
- K-Folds交差検証
- 層化Kフォールド交差検定
- Leave-One-Label-Out相互検証
- 置換交差検定を使用したランダムサンプリング
- [...]
の認識率を推定するcv2.FaceRecognizer
には、層化交差検定を使用することをお勧めします。なぜ誰かが他の相互検証方法を必要とするのかと疑問に思うかもしれません。アルゴリズムを使用して感情認識を実行したいとします。トレーニングセットに、アルゴリズムをテストする人の画像が含まれている場合はどうなりますか?あなたはおそらくその人に最も近いものを見つけるでしょうが、感情は見つかりません。このような場合、サブジェクトに依存しない相互検証を実行する必要があります。
層化k-FoldCrossValidation Iteratorの作成は、scikit-learnを使用すると非常に簡単です。
from sklearn import cross_validation as cval
# Then we create a 10-fold cross validation iterator:
cv = cval.StratifiedKFold(y, 10)
そして、私たちが選択できるさまざまな指標があります。今のところ、モデルの精度だけを知りたいので、呼び出し可能な関数をインポートしますsklearn.metrics.precision_score
。
from sklearn.metrics import precision_score
ここで、推定量を作成し、、、、およびをに渡すだけで済みます。estimator
これにより、交差検定スコアが計算されます。X
y
precision_score
cv
sklearn.cross_validation.cross_val_score
# Now we'll create a classifier, note we wrap it up in the
# FaceRecognizerModel we have defined in this file. This is
# done, so we can use it in the awesome scikit-learn library:
estimator = FaceRecognizerModel()
# And getting the precision_scores is then as easy as writing:
precision_scores = cval.cross_val_score(estimator, X, y, score_func=precision_score, cv=cv)
利用可能なメトリックは大量にあります。別のメトリックを自由に選択してください。
それでは、これらすべてをスクリプトに入れましょう!
検証.py
# Author: Philipp Wagner <bytefish@gmx.de>
# Released to public domain under terms of the BSD Simplified license.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the organization nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# See <http://www.opensource.org/licenses/bsd-license>
import os
import sys
import cv2
import numpy as np
from sklearn import cross_validation as cval
from sklearn.base import BaseEstimator
from sklearn.metrics import precision_score
def read_images(path, sz=None):
"""Reads the images in a given folder, resizes images on the fly if size is given.
Args:
path: Path to a folder with subfolders representing the subjects (persons).
sz: A tuple with the size Resizes
Returns:
A list [X,y]
X: The images, which is a Python list of numpy arrays.
y: The corresponding labels (the unique number of the subject, person) in a Python list.
"""
c = 0
X,y = [], []
for dirname, dirnames, filenames in os.walk(path):
for subdirname in dirnames:
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
try:
im = cv2.imread(os.path.join(subject_path, filename), cv2.IMREAD_GRAYSCALE)
# resize to given size (if given)
if (sz is not None):
im = cv2.resize(im, sz)
X.append(np.asarray(im, dtype=np.uint8))
y.append(c)
except IOError, (errno, strerror):
print "I/O error({0}): {1}".format(errno, strerror)
except:
print "Unexpected error:", sys.exc_info()[0]
raise
c = c+1
return [X,y]
class FaceRecognizerModel(BaseEstimator):
def __init__(self):
self.model = cv2.createFisherFaceRecognizer()
def fit(self, X, y):
self.model.train(X,y)
def predict(self, T):
return [self.model.predict(T[i]) for i in range(0, T.shape[0])]
if __name__ == "__main__":
# You'll need at least some images to perform the validation on:
if len(sys.argv) < 2:
print "USAGE: facerec_demo.py </path/to/images> [</path/to/store/images/at>]"
sys.exit()
# Read the images and corresponding labels into X and y.
[X,y] = read_images(sys.argv[1])
# Convert labels to 32bit integers. This is a workaround for 64bit machines,
# because the labels will truncated else. This is fixed in recent OpenCV
# revisions already, I just leave it here for people on older revisions.
#
# Thanks to Leo Dirac for reporting:
y = np.asarray(y, dtype=np.int32)
# Then we create a 10-fold cross validation iterator:
cv = cval.StratifiedKFold(y, 10)
# Now we'll create a classifier, note we wrap it up in the
# FaceRecognizerModel we have defined in this file. This is
# done, so we can use it in the awesome scikit-learn library:
estimator = FaceRecognizerModel()
# And getting the precision_scores is then as easy as writing:
precision_scores = cval.cross_val_score(estimator, X, y, score_func=precision_score, cv=cv)
# Let's print them:
print precision_scores
スクリプトの実行
上記のスクリプトは、Fisherfacesメソッドの精度スコアを出力します。画像フォルダを使用してスクリプトを呼び出す必要があります。
philipp@mango:~/src/python$ python validation.py /home/philipp/facerec/data/at
Precision Scores:
[ 1. 0.85 0.925 0.9625 1. 0.9625
0.8875 0.93333333 0.9625 0.925 ]
結論
結論は、オープンソースプロジェクトを使用するとあなたの人生が本当に簡単になるということです!サンプルスクリプトには、拡張すべき点がたくさんあります。たとえば、どのフォールドにいるかを確認するために、ログを追加することをお勧めします。ただし、これは必要なメトリックを評価するための開始点です。scikit-learnチュートリアルを読んで、それを実行し、上記のスクリプトに適合させる方法を確認してください。
ご覧のとおり、これら2つの優れたプロジェクトのインターフェースは非常に簡単であるため、OpenCVPythonとscikit-learnを試してみることをお勧めします。