Michael Beyeler:OpenCV と Python による機械学習プログラミング

作成日: 2020-12-11
最終更新日:

概要

「OpenCV + scikit-learn で機械学習プログラミングを実践マスター」とある。

感想

表題に OpenCV とあるが、機械学習がメインであり、OpenCV は従属する立場である。 しかも、機械学習の主要な内容は scikit-learn の説明に充てられている。 これはおそらく、OpenCV はコンピュータビジョンのあらゆる手法を網羅するため、 機械学習に関する分野は相対的に手薄になっていること、 機械学習に関しては scikit-learn が充実していることの両方があるだろう。 OpenCV についての全般的なことを知りたいのであれば、本書は不向きだろう。

さて、本書は 2018 年の出版で、2021 年の現在からみてさほど古くないはずなのだが、 記載の内容を確かめると違いがあったので、少し書いていく。 単純な誤植も含むが、下記サポートページ
https://book.mynavi.jp/supportsite/detail/9784839965303.html
で挙げられている誤りについては言及しない。

私が確認した環境は、OpenCV 4.5.0 または OpenCV 4.5.1 である。



p.110
In[8]: の入力
誤:min_max_scaler = preprocessing.MinMaxScaler(feature_range(-10,10))
正:min_max_scaler = preprocessing.MinMaxScaler(feature_range=(-10,10))

p.112
現在は preprocessing モジュールから Imputer を呼び出すことはできない。 その代わり、 impute モジュールから SimpleImputer を呼び出す。
https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html
https://scikit-learn.org/0.21/whats_new.html#id71
例:In[12] の最初の2行は、例えば次のように変更すればよい。

In [12]:from sklearn.impute import SimpleImputer
imp = SimpleImpute(strategy='mean')
p.115
次のようにウォーニングが出る。影響はないと思われる。
in [1]: x, y = np.random.multivariate_normal(mean, cov, 1000).T
	<ipython-input-4-f40628ea31ae>:1: RuntimeWarning: covariance is not positive-semidefinite.
	  x, y = np.random.multivariate_normal(mean, cov, 1000).T
	
p.117
In [17]: plt.quiver(mean[0], mean[1], eig[:,0],eig[:,1],zorder=3, scale=0.2, units='xy')
次のエラーが出る:
ValueError: Argument U has a size 2 which does not match 1, the number of arrow positions
このエラーの対処は、サポートページの正誤表によれば、次が正しいだろう。ただし、私は試していない。
plt.quiver(mean[0], mean[1], eig[0, 0], 0.6 * eig[1, 0],
 0.6 * eig[0, 1], eig[1, 1], zorder=3, scale=0.1, units='xy')
	

p.129
In [2]: img_bgr = cv2.imread('data/lena.jpg')
ここでは、カレントディレクトリに data というディレクトリがあり、 その下に lena.jpg という画像ファイルが存在することを前提としている。 本書の p.27 の方法で git clone を著者のホームページで実行していれば、 opencv-machine-learning/data の配下に、lena.jpg というファイルがあるはずだ。 もしなければ、この行ではなく、次の行 In [3]: でエラーが生じるので注意のこと。 なお、lena.jpg は有名なレナの画像であるが、レナ自身はこの画像を使わないように願っているということなので、 他の画像を使うのがいいだろう。

p.132
In [6]: sift = cv2.xfeatures2d.SIFT_create()
次の警告が出る:
[ WARN:0] global /home/conda/feedstock_root/build_artifacts/libopencv_1610135932067/work/opencv_contrib/ →
		modules/xfeatures2d/misc/python/shadow_sift.hpp (13) SIFT_create DEPRECATED: →
		cv.xfeatures2d.SIFT_create() is deprecated due SIFT tranfer to the main repository. →
		https://github.com/opencv/opencv/issues/16736
これに対しては、次のように変更すればよい。
In [6]: sift = cv2.SIFT.create()

SURF

p.135
In [14]: surf = cv2.xfeatures2d.SURF_create()
次のエラーが出る:
error: OpenCV(4.5.1) /tmp/pip-req-build-jr1ur_cf/opencv_contrib/modules/xfeatures2d/src/surf.cpp:1027: →
	error: (-213:The function/feature is not implemented) This algorithm is patented and is excluded →
	in this configuration; Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library in function 'create'
	
SURF に関しては英語によって上記の解決法(CMake オプションで OPENCV_ENABLE_NONFREE を設定してリビルドすること)
が提案されているが、私は試していない。
p.149
In[16]: dtree = cv2.ml.dtree_create()
次のエラーが出る:
module 'cv2.ml' has no attribute 'dtree_create' 
代わりに、次の関数を使う。
dtree = cv2.ml.DTrees_create()
なお、著者の github のページでは、上記の通り修正されている。
	
p.149
In[17]: dtree.train(X_train, cv2.ml.ROW_SAMPLE, y_train)
AttributeError: 'builtin_function_or_method' object has no attribute 'train'
	
'train' という属性はない、と言われている。
https://docs.opencv.org/4.5.1/d8/d89/classcv_1_1ml_1_1DTrees.html
を見ると、create() メンバー関数で次のように説明されている:
Creates the empty model.
	
The static method creates empty decision tree with the specified parameters. 
It should be then trained using train method (see StatModel::train). 
Alternatively, you can load the model from file using Algorithm::load<DTrees>(filename).
	
ということは、訓練には StatModel::train のほうを使わないといけないようだ。
なお、著者の github のページでは、OpenCV 3 のバグとなっていて、これ以上の記載がない。
こうなると私の手に余る。5.1.1 節の理解はここで断念した。
	
p.151
In [26]: with open("tree.dot", 'w'):
	...:     f = tree.export_graphviz(clf, out_file=f)
次のエラーが出る:
NameError: name 'clf' is not defined
	
それはそうだ。github の著者のページでは次のようになっている:
	
In [26]: with open("tree.dot", 'w') as f:
	f = tree.export_graphviz(dtc, out_file=f,
								feature_names=vec.get_feature_names(),
								class_names=['A', 'B', 'C', 'D'])
	
これならばうまくいく。
	
p.160
In [5]: import sklearn.model_selection as ms
			X_train, X_test, y_train, y_test = 
			ms.train_test_split(datapre, target, test_size=0.2, random_state=42)
	この通り入力すると、次であっと驚くことになる。
In [6]: X_train.shape, X_test.shape
Out[6]: ((16, 10), (4, 10))
	
これは、In [5] の入力に誤りがあるためだ。次のようにすればうまくいく:
In [5]:import sklearn.model_selection as ms
    X_train, X_test, y_train, y_test = ms.train_test_split(data.data, data.target, test_size=0.2, random_state=42)
Out[6]: ((455, 30), (114, 30))
	

p.188
枠囲みにある notebook/data/pedestrians128x64.tar.gz は、 正しくは notebooks/data/chapter6/pedestrians128x64.tar.gz である。

p.193
In [8] を実行すると、次のエラーが出る:
Could not find image data/chapter6/pedestrians128x64/per00000.ppm
これは、In [8] の次の文に原因がある。
for i in random.sample(range(900), 400)
この range(900) は、具体的には 0 以上 900 以下の整数を意味する。したがって、0 を含む。 一方、per?????.ppm には per00000.ppm の画像はないので、エラーとなる。 これを解決するには、range(1,900)と書き直せばよい。すると、0は含まれなくなる。


p.200
In [24]: rho, _, _ = svm.getDecisionFunction(0)
sv = svm.getSupportVectors()
hog.setSVMDetector(np.append(sv.ravel(), rho))
すると次のエラーになる:
error Traceback (most recent call last)
<ipython-input-85-a74ae042f8e7> in <module>
----> 1 hog.setSVMDetector(np.append(sv.ravel(), rho))
error: OpenCV(4.5.1) /tmp/pip-req-build-jr1ur_cf/opencv/modules/objdetect/src/hog.cpp:121: error: (-215:Assertion failed) checkDetectorSize() in function 'setSVMDetector'
hog の文は、次が正しい。著者の github には修正後のコードがある。
hog.setSVMDetector(np.append(sv[0, :].ravel(), rho))

P.200
In [25]: found=hog.detectMutliScale(img_test)
次のエラーが出る。
AttributeError: 'cv2.HOGDescriptor' object has no attribute 'detectMutliScale'
これは、本書の同じページの TIP で書かれていることから推察されることなのだが、 detectMultiScale が有効か否かがバージョンによって変わりうるので、きっとこのような属性がないのだろう。

p.228
In [10]:X = counts.fit_transform(data['text']).values)
この文の実行には時間を要する。1分から5分は見ておくべきだろう。 このあたりは毎回対話環境で入力するところではないので、 適当なコードを email.py などとしてまとめて、ipython を起動したのちに、
%run email.py
のようにするといい。

p.230
In [16]: model_norm.train(X_train_small, cv2.ml.ROW_SAMPLE, y_train_small)
これを実行したら、いきなり下記のエラーが表示され、ipython も終わってしまった。
浮動小数点例外
$
著者の github を見てみると、こんなことが書かれている。

It appears that NormalBayesClassifier is broken in OpenCV 3.1 (segmentation fault). As a result, the kernel will die.

そういうことか。

p.238
In [2]: from sklearn.datasets.samples_generator import make_blobs
/home/username/anaconda3/lib/python3.8/site-packages/sklearn/utils/deprecation.py:143: FutureWarning: The sklearn.datasets.samples_generator module is deprecated in version 0.22 and will be removed in version 0.24. The corresponding classes / functions should instead be imported from sklearn.datasets. Anything that cannot be imported from sklearn.datasets is now part of the private API. warnings.warn(message, FutureWarning)
どうやら、The sklearn.datasets.samples_generator は非推奨と言っているらしい。次でいいだろう。
In [2]: from sklearn.datasets import make_blobs

p.248
In [13]: plt.plot(kvals, compactness, 'o-', linewidth=4, markersize=12)
次のエラーが出る
(中略)
ValueError: x and y must have same first dimension, but have shapes (8,) and (1,)
これは、p.248 の最初のプログラムコードで、compactness.append(c) のインデントが for k in kvals: の先頭と同じ位置にあったためである。このように書くと、compactness.append(c) はループから外れてしまう。 したがって、compactness.append(c) のインデントは、c, _, _ = ... の行のインデントと同じとしないといけない。

pp.277-p278
def fit のメソッドの定義が2行しかないが、これでは学習できない。 一方、p.278 の二重ループが p.277 の predict メソッドの後に実行されるように書かれているが、 正しくはこの二重ループは predict メソッドの初期化2行のあとに行われるべきコードだ。 正しいコードは著者の github にある。

p.278
sklearn.datasets.samples_generator が非推奨であるのは p.238 と同様。

よしなしごと

p.223 に、スパムフィルターについて書かれている。迷惑メールをスパム SPAM というのは知っていたが、 非迷惑メールを HAM ということは知らなかった。HAM は何かの頭文字語だろうか、と最初思ったが、 考えてみれば HAM は塩漬け肉であるハムそのものだった。

関連書籍

書誌情報

書 名OpenCV と Python による機械学習プログラミング
著 者Michael Beyeler
発行日2018 年 8 月 30 日 初版 第 1 刷
発行元マイナビ出版
定 価3580 円(本体)
サイズ ページ
ISBN978-4-8399-6530-3
その他越谷市立図書館南部図書室で借りて読む

まりんきょ学問所コンピュータの部屋コンピュータの本ニューロコンピューティング・機械学習 > Michael Beyeler:OpenCV と Python による機械学習プログラミング


MARUYAMA Satosi