付録 A

matplotlib を用いたプログラムの解説
A.1

はじめに

本書では、グラフ描画用に matplotlib というライブラリを用いました。付録 A では matplotlib を用いたプログラムの概略を解説します。必ずしも網羅的な解説にはなっておりませんので、プログラム中のコメント文による解説もあわせてご覧ください。

A.2

２クラスへの分類における背景の色分け

4.4 において、線形サポートベクトルマシンによるアヤメの分類（２特徴量、２クラス）を２つのプログラムで体験しました。そのうち ml-04-02-2feat2class01.py をプロ
グラム 4-2（107 〜 110 ページ）として解説しました。
プログラム 4-2 の 29 行目以降では、アヤメの２つのクラスを背景に異なる色を描画することで区別しています。
その部分の概略について解説します。

●背景描画によるクラス分け
まず背景に色をつけるため、グラフの縦方向と横方向を
1

付録A

それぞれ 500 ずつ区切って 250000 個のグリッド（格子）を作ります。そしてその一つ一つのグリッドがどちらのクラスに属するかを予測します。
グリッド上の点からなる NumPy 配列 Xg に対して、それぞれの点がどちらのクラスに属するかを予測し、その結果を Z に格納しているのが下記の行です。
47 Z = clf.predict(Xg)

データ数 250000、特徴量２つの NumPy 配列 Xg と、要
素 数 250000 の NumPy 配 列 Z と の 関 係 は 100 ペ ー ジ の 図
4-6（A）の X と y の関係に似ています。
グリッド上の点の属するクラスを格納した Z をもとに、背景に色を塗っているのが下記の２行です。
54 cmap01 = ListedColormap([(0.5, 1, 1, 1), (1, 0.93, 0.5, 1)])57 plt.pcolormesh(XX, YY, Z==0, cmap=cmap01)

まず、２つの色を定義しています。０番目の色が (0.5,
1, 1, 1) で水色、１番目の色が (1, 0.93, 0.5, 1) でオレンジ色です。色は 0 から 1 の範囲の数字４つで表現し、順に
赤、 緑、 青、 不 透 明 度 の 強 さ を 表 し ま す。 そ し て、
「Z==0」（Z が 0 に等しい）という条件が満たされる点には１番目の色のオレンジで、満たされない点には０番目の色の水色で色をつけています。
2

●データ点の描画
次にデータ点の描画です。すべての点は X に格納されています。これらをクラスごとに色分けして表示するため、下記の２行でクラス 0（iris setosa）に属する点のみを集めた Xc0 とクラス 1（iris versicolor）に属する点のみを集めた Xc1 とに分けています。
66 Xc0 = X[y==0]
68 Xc1 = X[y==1]

そして、Xc0 をオレンジ色の点で描画しているのが下記の命令です。
71 plt.scatter(Xc0[:,0], Xc0[:,1], c=’#E69F00’, linewidths=0.5,

edgecolors=’black’)「Xc0[:,0], Xc0[:,1]」はそれぞれ Xc0 の横方向の数値（外花被片の長さ）と縦方向の数値（外花被片の幅）を112 ページの図 4-9（C）の方法で取り出しています。この点が、塗りつぶしをオレンジ色（#E69F00）で、点の枠線を黒（black）で表示されています。色の指定の方法として、これまで登場したように「４つの数字の組み合わせ（(0.5, 1, 1, 1)）」、「Web ページを作るときなどに使われる表記（#E69F00）」、「色名（black）」など複数の方法が利3

付録A用できます。
最後に、サポートベクトルを取り出して赤い枠を表示しているのが下記の命令です。
76 SV = clf.support_vectors_
78 plt.scatter(SV[:,0], SV[:,1], c=(0,0,0,0), linewidths=1.0,

edgecolors=’red’)「clf.support_vectors_」でサポートベクトル全体を取り出せること、塗りつぶしの色として不透明度が０の透明色を指定していることなどがわかります。サポートベクトル
マシンの利用においてサポートベクトルを明示的に表示する必要は必ずしもありませんが、このプログラムでは 4.2
での解説との対応をつけるためにサポートベクトルを表示
しました。

A.3

損失関数の時間変化のグラフ化

５章で解説した多層ニューラルネットワークでは、ネッ
トワークの学習過程における損失関数の時間変化を確認す
る こ と が 重 要 な の で し た。 プ ロ グ ラ ム ml-05-01-2feat
3class-nn.py において、そのグラフ化を matplotlib で実現している部分を解説します。
損失関数の時間変化をグラフ化しているのは ml-05-012feat3class-nn.py の中の下記の部分です。
4

93 #損失関数のグラフの軸ラベルを設定
94 plt.xlabel(’time step’)
95 plt.ylabel(’loss’)
96
97 # グラフ縦軸の範囲を0以上と定める
98 plt.ylim(0, max(clf.loss_curve_))99
100 # 損失関数の時間変化を描画
101 plt.plot(clf.loss_curve_)
102
103 # 描画したグラフを表示
104 plt.show()

縦と横の軸にラベルを設定し、さらに縦軸の最小値を 0にするよう設定したうえで、「clf.loss_curve_」をグラフ表示しています。
「clf.loss_curve_」は学習開始時から終了時までの損失関数の値が格納された NumPy 配列です。

A.4

多数の手書き数字を描画する

６章で体験した手書き数字の分類では、サンプルに含まれる手書き数字画像を表示することでそのイメージをつかみました。この部分にも matplotlib が使われています。それを実現するプログラム ml-06-02-images.py の概略を説
明します。

5

付録A

●プログラムの全体
ml-06-02-images.py をすべて掲載したのが下記のプロ
グラム A-1 です。
1 # -*- coding: utf-8 -*2 from sklearn import datasets
3 import numpy as np
4 import matplotlib.pyplot as plt
5
6 # 手書き数字のデータをロードし、変数digitsに格納
7 digits = datasets.load_digits()
8
9 # 特徴量のセットを変数Xに、ターゲットを変数yに格納
10 X = digits.data
11 y = digits.target
12
13 # 手書き数字の画像表現を変数imagesに格納
14 images = digits.images
15
16 # 表示エリアの背景をシルバーにセット
17 fig = plt.figure()
18 fig.patch.set_facecolor(’silver’)
19
20 # 0〜9の10枚の画像をそれぞれ3枚ずつ、計30枚描画
21 for i in range(10):22for j in range(3):23# 数字iの画像のうち、j枚目を取り出す

24

img = images[y==i][j]625#ランダムに取り出したい場合は下記を有効に

26#img = images[y==i][np.random.randint(0, len(images[y==i]))]

27# 縦5x横6の画像表示エリアのうち、3*i+j+1番目に描画

28

plt.subplot(5, 6, 3*i + j + 1)

29

# グラフとしての軸は描画しない

30

plt.axis(’off’)31# 白黒を反転した状態で描画

32

plt.imshow(img, cmap=plt.cm.gray_r, interpolation=’nearest’)33# 各画像にタイトルを描画

34

plt.title(’Data {0}’.format(i))

35
36 # 画像間に余裕をもたせて描画
37 plt.tight_layout()
38
39 # 描画した内容を画面表示
40 plt.show()プログラム A-1

０ 〜９の画像をそれぞれ３枚ずつ表示する ml-0602-images.py●プログラムの解説
まず、変数 digits に格納された 1797 枚の画像を下記の命令で変数 images に格納することができます。
14 images = digits.images

この images から、0 〜 9 の 10 個の数字に対応する画像
7

付録Aを３枚ずつ取り出すには、下記のように二重の for 文を用います。これを入れ子構造とも言います。
21 for i in range(10):22for j in range(3):この for 文の内部では、0 〜 9 の数字に対応する変数 i お
よび３枚の画像の何枚目かを示す変数 j が利用可能になります。そして、数字 i の j 枚目の画像を取り出して変数 imgに格納しているのが下記の命令です。
24

img = images[y==i][j]そしてこの画像 img を matplotlib の imshow という画像表示用の関数で表示しています。
なお、このプログラムでは各数字の画像のうち、先頭の３枚しか見ることができません。とはいえ、それぞれ約180 枚ある数字のすべてを表示することは現実的ではありません。
そこで、閲覧する３枚の画像がランダムに選ばれるようにもしてあります。プログラム中の下記の部分に着目して
ください。

25
26

#ランダムに取り出したい場合は下記を有効に#img = images[y==i][np.random.randint(0, len(images[y==i]))]

8

この 26 行目の先頭の「#」のみを削除して保存すると、３枚の画像がランダムに選ばれるようになります。その際、削除するのは先頭の「#」１文字のみとしてください。その前にある８文字の空白文字のうち１文字でも削除すると、エラーが出てプログラムを実行できなくなるでしょう。これは、プログラミング言語 Python では行の先頭の空白文字の個数によりプログラムの構造が決まるためです。
なお、上の命令の「np.random.randint(0, len(images[y==i]))」は「0」から「len(images[y==i])-1」の範囲のランダムな整数、という意味になります。178 枚ある数字
0 の画像の場合ですと、0 から 177 の整数のどれかがランダムに選ばれます。そのようにしてランダムに選ばれた画像
が画面に表示されます。
×ボタンでウインドウを閉じてからプログラムを再実行する、ということを何度か繰り返すと、その度にランダムに選ばれた数字が表示されます。それらを眺めると、さま
ざまな個性を持った数字を見ることができるでしょう。

9

付録B

付録 B

OpenCV を用いたプログラムの解説
B.1

はじめに

本書の８章では、画像処理用ライブラリ OpenCV を用いた演習を行いました。付録 B では、それらを実現したプログラムの概略を解説します。OpenCV はそれだけで１冊の本が書けるほど大規模なライブラリです。最初から全てを理解しようとするのではなく、時間をかけて少しずつ理解することを目指すのが良いと筆者は考えます。

B.2

手の形の二値化

8.4 にて、手の形を白、それ以外を黒とする画像の二値化を行い、その流れを紹介しました。ここではそれをOpenCV で実現するプログラム ml-08-02-binary.py の解説を行います。

●HSV形式への変換を行う命令
カメラから得られた画像を HSV 形式へ変換し、色相（Hue）と彩度（Saturation）に対応する画像 h_channel とs_channel を得ているのが下記の４行です。
10

54 hsv = cv2.cvtColor(stream.array, cv2.COLOR_BGR2HSV)
56 hsv_channels = cv2.split(hsv)
57 h_channel = hsv_channels[0]58 s_channel = hsv_channels[1]「hsv = cv2.cvtColor(stream.array, cv2.COLOR_BGR2HSV)」 と い う 命 令 に よ り、 カ メ ラ か ら 得 た 画 像
stream.array を HSV 形式の画像 hsv に変換しています。その 次 の「hsv_channels = cv2.split(hsv)」 命 令 に よ り
HSV 形式の画像 hsv を色相（Hue）、彩度（Saturation）、
明度（Value）の３つに分解し、hsv_channels という変数に収めています。このうち０番目の要素を色相に対応する
h_channel と し て、 １ 番 目 の 要 素 を 彩 度 に 対 応 す る s_channel として利用可能にしているのが 57 行目、58 行目です。明度の情報はこのプログラムでは利用しないのでした。

●h_channelの二値化を行う命令
h_channel の二値化を行っているのが下記の３命令です。

61 h_binary = cv2.GaussianBlur(h_channel, (5,5), 0)
65 ret,h_binary = cv2.threshold(h_binary, hmax, 255, cv2.

THRESH_TOZERO_INV)

11

付録B

66 ret,h_binary = cv2.threshold(h_binary, hmin, 255, cv2.

THRESH_BINARY)

まず、61 行目は h_channel のデータのノイズ除去を行っています。そして、65 行目は hmax 〜 255 の値を持つピク
セルを 0（黒）にする、という命令を実行しています。そして、66 行目では hmin 〜 255 の値を持つピクセル（実際には hmin 〜 hmax の値のピクセルしかありません）を 255（白）にしそれ以外を 0（黒）にする、という命令を実行しています。以上により、264 ページの図 8-8（A）の処理が実現されます。

●s_channelの二値化を行う命令
s_channel の二値化は下記の１命令で実現されます。
69 ret,s_binary = cv2.threshold(s_channel, smin, 255, cv2.

THRESH_BINARY)

smin 〜 255 の値を持つピクセルを 255（白）にしそれ以外を 0（黒）にする、という命令です。それにより、図8-8（B）の処理が実現されます。

●h_binaryとs_binaryの共通部分hs_andを生成する命令
h_binary と s_binary の共通部分 hs_and を生成する命令
12

は下記の１行で実現されます。
73 hs_and = h_binary & s_binary

すでに述べたようにこの処理は AND という演算で実現され、その記号は「&」です。

●白領域のうち最大のもののみ残す命令
白領域のうち最大のものを残す命令はやや複雑で、下記のように複数の命令の組み合わせで実現されます。
77 img_dist, img_label = cv2.distanceTransformWithLabels(255-hs_and, cv2.cv.CV_DIST_L2, 5)78 img_label = np.uint8(img_label) & hs_and
80 img_label_not_zero = img_label[img_label != 0]
82 if len(img_label_not_zero) != 0:83

m = stats.mode(img_label_not_zero)[0]84 else:85m = 0

87 hand = np.uint8(img_label == m)*255「cv2.distanceTransformWithLabels」は、対象となる画像のそれぞれのピクセルから、ゼロが格納されたピクセル
13

付録B

までの距離を計算する関数です。さらに、どのゼロに最も近いかにもとづいて全ピクセルにラベルをつけてグループ分けする機能もあります。
ここでは、ゼロ（黒）までの距離ではなく、255（白）までの距離としたいので、0（黒）と 255（白）を入れ替えた「255-hs_and」を「cv2.distanceTransformWithLabels」
への入力としています。
その結果、もともと白であった点への距離を格納した画
像 img_dist と、白であった点への近さでグループ分けした
img_label とが得られます。ここで必要なのは img_label です。
img_label には、すべてのピクセル値に対して１以上の整数値がラベルとして格納されています。そのラベルはもともとの白い領域ごとにグループ分けされています。ただし、このグループにはもともと黒い領域だった点も含まれていますので、「img_label = np.uint8(img_label) & hs_
and」のように AND 命令を利用し、黒い領域をすべて０で埋め尽くします。
「img_label_not_zero = img_label[img_label != 0]」を実行すると、黒い領域である０以外のラベルが NumPy 配列として抜き出されます。
「m = stats.mode(img_label_not_zero)[0]」を実行すると、最も個数の多いラベルが変数 m に格納されます。これは、統計学の最頻値（モード）を計算する機能を SciPyの stats パッケージから呼び出すことで実現しています。
あとは、ラベル m に対応する白領域のみに 255 を格納
14し、他の点を 0 にすれば求めたい画像 hand が得られます。

B.3

手の回転と左右反転

8.5 では、学習用の手の画像の枚数を増やすため、手の回転と左右反転を行いました。それを OpenCV で実現しているプログラムは ml-08-03-learn.py です。ここではその概略を解説します。
手 の 回 転 と 左 右 反 転 を 実 現 し て い る の は ml-08-03learn.py の下記の部分です。コメント文は省略して表示しています。
65 for flip in [0, 1]:66

if flip == 1:67

img = cv2.flip(img, 1)

68

for angle in [-80, -60, -40, -20, 0, 20, 40, 60, 80]:70

rot_mat = cv2.getRotationMatrix2D((cx, cy), angle, 1.0)72

img_rot = cv2.warpAffine(img, rot_mat, (lx, ly), flags=cv2.INTER_CUBIC)

プログラムの構造としては左右反転するかしないかの２通りを表す for 文「for flip in [0, 1]:」と、回転角度を
９通りに変更する for 文「for angle in [-80, -60, -40,
-20, 0, 20, 40, 60, 80]:」の入れ子構造になっています。これにより、2 × 9 ＝ 18 倍の枚数の画像を生成するこ
15

付録B

とになります。
変数 flip が１のとき、すなわち左右反転を行う際は命令「img = cv2.flip(img, 1)」によって元画像 img を左右反転しています。与えている数字の 1 は左右の反転であることを意味します。0 を与えれば上下反転、− 1 を与えれば左右と上下両方の反転となります。
回転については、数学の概念である行列（マトリックス）がまず必要になります。
「rot_mat = cv2.getRotationMatrix2D((cx, cy), angle,
1.0)」により、角度 angle の回転行列 rot_mat が用意されます。与えている (cx, cy) は回転の中心ピクセルを表します。(cx, cy) には (160, 120) が格納されており、これは 320
× 240 のサイズの画像の中心位置です。つまり、画像の中心点周りに画像が回転するということです。与えている数
字の「1.0」は画像の拡大率を表しており、1.0 という数字
は画像の拡大縮小は行わず回転のみを行うことを意味します。
最後に、回転行列 rot_mat を用いて実際に画像の回転を行うのが命令「img_rot = cv2.warpAffine(img, rot_mat,(lx, ly), flags=cv2.INTER_CUBIC)」です。(lx, ly) は画像のサイズを表しており、ここでは (320, 240) が格納されています。回転された画像は img_rot に格納されます。

16

B.4

手の切り出しと縮小

8.5 では学習用の手の画像を 810 枚まで増やしたあと、それらを OpenCV を用いて縮小して機械学習に入力するベクトルを作成しています。その部分の解説を行います。

●手の切り出しと縮小を行うプログラム
手の切り出しと縮小を行い、最終的にベクトル x の作成までを行う命令は、ml-08-03-learn.py の下記の部分、すなわち、getⅠmageVector という関数全体となります。関
数とは、繰り返し利用される処理を、部品のようにまとめたものです。この関数には、回転処理を施された手の画像が渡されますので、それをもとにベクトル x が計算されます。
いつも通り、コメント文を省略して表示しています。
19 def getImageVector(img):
21

nonzero = cv2.findNonZero(img)

23xx, yy, ww, hh = cv2.boundingRect(nonzero)25

img_nonzero = img[yy:yy+hh, xx:xx+ww]27

img_small = np.zeros((sh, sw), dtype=np.uint8)

17

付録B

29

if 4*hh < ww*3 and hh > 0:30

htmp = int(sw*hh/ww)31

if htmp>0:32

img_small_tmp = cv2.resize(img_nonzero, (sw, htmp), interpolation=cv2.INTER_LINEAR)33

img_small[(sh-htmp)/2:(sh-htmp)/2+htmp, 0:sw] = img_small_tmp

34

elif 4*hh >= ww*3 and ww > 0:35

wtmp = int(sh*ww/hh)

36

if wtmp>0:37

img_small_tmp = cv2.resize(img_nonzero, (wtmp, sh), interpolation=cv2.INTER_LINEAR)38

img_small[0:sh, (sw-wtmp)/2:(sw-wtmp)/2+wtmp] = img_small_tmp

40

return np.array([img_small.ravel()/255.])

このうち、切り出しを行っているのは下記の３行です。
21

nonzero = cv2.findNonZero(img)

23xx, yy, ww, hh = cv2.boundingRect(nonzero)25

img_nonzero = img[yy:yy+hh, xx:xx+ww]「nonzero = cv2.findNonZero(img)」により、283 ページの図 8-15 の左端のような画像のうち、ゼロ（黒）ではな
い点の座標がすべて変数 nonzero に格納されます。
18

「xx, yy, ww, hh = cv2.boundingRect(nonzero)」により、nonzero に含まれる座標をすべて包含する長方形の左
上点の座標 (xx, yy) と幅 ww および高さ hh が得られます。
この長方形は、283 ページの図 8-15 の左端の画像の赤い
長方形のことです
この長方形内の画像を変数に格納しているのが「img_nonzero = img[yy:yy+hh, xx:xx+ww]」 で す。NumPy 配列の領域指定により画像を切り出しています。
次に、切り出された画像を縮小して最終的に変数 img_
small に格納します。img_small のサイズは幅 16 ×高さ 12なのでした。ここでは図 8-15（A）および（B）における２つの矢印に相当する処理をまとめて行います。
ただし、プログラムとしては画像のアスペクト比による条件分けが必要なのでやや煩雑になっています。アスペク
ト比とは、画像の幅と高さの比のことです。最終的な画像
img_small の ア ス ペ ク ト 比 は 16：12、 す な わ ち 4：3 です。この 4：3 よりも手が横長の場合と縦長の場合とがあります。横長の場合が図 8-15
（A）に該当し、手の上下に黒い余白が生まれます。縦長の場合が図 8-15
（B）に該当し、手の左右に黒い余白が生まれます。プログラムでは「4*hh < ww*3」の場合が横長の場合に対応し、「4*hh >=
ww*3」の場合が縦長の場合に該当します。
手が横長の場合に絞って解説します。まず以下の命令により幅 16 ×高さ 12 の黒い画像をあらかじめ用意しておきます。sw に 16、sh に 12 が格納されています。

19

付録B

27

img_small = np.zeros((sh, sw), dtype=np.uint8)

そして、この img_small に手の赤い四角の領域を縮小しながら貼り付けているのが以下の４行です。
30

htmp = int(sw*hh/ww)31

if htmp>0:32

img_small_tmp = cv2.resize(img_nonzero, (sw, htmp), interpolation=cv2.INTER_LINEAR)33

img_small[(sh-htmp)/2:(sh-htmp)/2+htmp, 0:sw] = img_small_tmp

htmp は 283 ページの図 8-15
（A）右の画像において手の縦方向の範囲を表します。図 8-15
（A）右では htmp は 9 です。そして、「img_small_tmp = cv2.resize(img_nonzero,(sw, htmp), interpolation=cv2.INTER_LINEAR)」により、手の領域を幅 sw、高さ htmp に縮小した画像 img_small_
tmp を得ます。図 8-15
（A）
では sw は 16、htmp は 9 ですね。
最後に、
「img_small[(sh-htmp)/2:(sh-htmp)/2+htmp, 0:sw] = img_small_tmp」という命令により、あらかじめ用
意 し て お い た 幅 16 × 高 さ 12 の img_small に img_small_
tmp を貼り付けています。配列の領域指定により貼り付け
ています。

20

●ベクトルの完成
最 後 に、 幅 16 × 高 さ 12 の 画 像 img_small か ら 1 × 192（192 次元ベクトルが１個）の NumPy 配列を生成し、関数の結果として返しているのが下記の命令です。img_small
は 0（黒）から 255（白）までの整数値を持ちますので、これをすべて 255 で割って 0 から 1 の実数値とします。それにより、ニューラルネットワークへの入力として適したベクトルになります。
40 return np.array([img_small.ravel()/255.])

なお、ここで得られたベクトルは、下記のように分類器
への入力データ X へと加工されます。
まず、あらかじめ入力データ X を空のデータとして初期化しておきます。ターゲット y の初期化も一緒に行っています。
43 X = np.empty((0,sw*sh), float)
44 y = np.array([], int)

この X と y に下記のようにデータを追加していきます。
76 X = np.append(X, img_vector, axis=0)77 y = np.append(y, hand_class)

21

付録B

img_vector は getⅠmageVector によって返された 1 × 192の NumPy 配列、hand_class は、0（グー）、1（チョキ）、
2（パー）に対応する整数値が格納されています。
「●手の回転と左右反転」（279 ページ）で述べたように、手の画像は画像処理により得たものも合わせて 810 枚ありますから、最終的に X は 810 × 192（192 次元ベクトルが 810 個 ） の NumPy 配 列、y は 810 個 の 整 数 か ら な る
NumPy 配列となります。

22

付録 C

自分の手の画像を学習用データとする方法
C.1

はじめに

８章にて、Raspberry Pi に接続したカメラに手をかざし、それがグー、チョキ、パーのいずれかを分類する課題を取り扱いました。その際、学習に用いる画像としては筆者が用意した 45 枚の画像を使いました。付録 C では、手の画像を皆さんで用意する方法を紹介します。
８章で用いたプログラム ml-08-02-binary.py を用いますので、８章の内容を十分理解しているという前提で以下の解説を進めます。

C.2

プログラムを実行して画像を保存する

ml-08-02-binary.py を実行するためには、ターミナルを起動して、下記のコマンドを実行するのでした。
python ml-08-02-binary.py
な お、3.3 に お い て 本 章 の サ ン プ ル フ ァ イ ル を blue
backs ディレクトリに展開した方は、上記コマンドを実行する前に「cd bluebacks」コマンドを実行し、カレントデ
23

付録C

ィレクトリを bluebacks ディレクトリに変更することにも注意してください。
このとき、258 ページの図 8-6 のように二値化された手が現れるのでした。手の領域だけを白く表示するための方法は 8.4 にて解説されています。
このプログラムを終了するためには、図 8-6 のウインド
ウをマウスでクリックし、ウインドウ上部のバーが水色になった状態でキーボードの「q」をタイプすれば良いことも覚えておきましょう。
このとき、表示されている手の画像をファイルに保存するために必要な知識を列挙すると下記のようになります。
◦画 像は ml-08-02-binary.py と同じディレクトリに img_
guXXX.png、img_chokiXXX.png、img_paXXX.png という名称で保存される。
「XXX」の部分はファイルの番号となっており、それぞれの手の画像が増えるたびに「000」→「001」→「002」……と増えていく◦図 8-6 のウインドウ上でキーボードの「g」をタイプす
ると、グー画像として img_guXXX.png という名称で保存される。同様に、「c」をタイプするとチョキ画像として img_chokiXXX.png という名称で保存される。「p」
をタイプするとパー画像として img_paXXX.png という名称で保存される◦画像を保存する順番は任意。すなわち、最初にグーだけを連続して保存しても構わないし、グー、チョキ、パーと順番に保存しても構わない
24

◦画 像を保存する際は、274 ページの図 8-12 を参考にさ
まざまな状況を保存すると良い。ただし、8.5 で解説したように、回転と左右反転した画像についてはプログラ
ムで生成するので考慮しなくて良い◦グー、チョキ、パーのそれぞれの画像の枚数は同じであることが学習を実行する上で望ましいが、それぞれ異なる枚数でもあとの学習プログラムは問題なく動作する◦必 要な枚数の画像を保存し終えたら、258 ページの図
8-6 のウインドウ上でキーボードの「q」をタイプして
ml-08-02-binary.py を終了する。一般に、画像の枚数が多いほど分類の性能は上がることが期待されるが、そのぶん学習に長い時間がかかる
以上が保存に関する注意点です。ml-08-02-binary.pyと同じディレクトリに画像が保存されているか、ファイルマネージャで確認してみましょう。

C.3

保存した画像を移動して学習する

保存した画像は ml-learn ディレクトリに移動しないと、学習時に読み込まれません。ただし、ml-learn ディレクト
リには筆者が用意した画像が存在します。それをあらかじ
め削除するか、あるいはどこか別にディレクトリを作成してその中に移動して退避させておきましょう。そうしてから、皆さんが保存した画像を ml-learn ディレクトリに移動
します。
25

付録C

以上の操作は、すべてファイルマネージャを用いて行うことができます。
あとは 8.5 の解説に従って ml-08-03-learn.py を実行す
ると、皆さんが保存した画像を用いて学習が行われます。
ただし、以下の１点だけ注意してください。
◦画像の番号は連続していないと ml-08-03-learn.py から読み込まれない。たとえば、グーの画像として img_
gu000.png、img_gu001.png、img_gu002.png、img_
gu004.png……のように 003 番が飛ばされている場合、連 続 し た img_gu002.png ま で の 画 像 し か 読 み 込 ま れず、img_gu004.png 以降の画像は無視される

26