#データセットとは
機械学習におけるデータセットとは、学習に必要な情報がまとまっている「You これで機械学習やっちゃいなよ」セットである。様々なデータセットがネット上に公開されており、
MNISTの手書き数字のデータセットなどはとても有名である。
#データセット生成方法の情報が少ない
web上のデータセットを用いて機械学習の勉強をする際には問題にならないが、勉強を進めていくと「これは自分オリジナルだぜ」的な機械学習を試しにさせてみたくなるのが漢の性である。
しかし、ここで壁にぶち当たる。。。
機械学習自体の情報に比べて、データセット生成に関するが圧倒的に少ない!と拙者は思う。プロフェッショナルな方々は「データセット?そんなん朝飯前に作れるっしょ?」的な高飛車な感じなのかもしれませんが、拙者みたいな「夏です。機械学習始めました。」みたいな初心者にとっては大事なのである。
なのでどうにかこうにか画像判別用データセット生成に辿り着いたので、その方法を一例として公開しようと思ったわけである。
#画像データセット生成までのフロー
拙者は愛すべき美女達(満島ひかり、沢尻エリカ、宮崎あおい、新垣結衣、本田翼)のクラス分類をしたかったので、美女データセットを作成しました。流れは以下になります。
- 画像収集
- 顔抽出
- データセットの形式に変換
##画像の集め方、顔抽出
画像の収集方法、顔抽出方法は
美女を見分けられない機械はただの機械だ:OpenCV, Python3による画像からの顔抽出
に書いてあるのでそちらを参照してください。私は合計1500枚ほどの美女の顔抽出画像を集めました。(照)
#データセットの形式に変換
データセット生成に当たり画像認識で坂道グループの判別AIの作成を大変参考にさせていただきました。
##データセットの呼び出し方を考える
上記の記事ではデータのNumpy配列オブジェクトをpickle漬けにして保存している。しかし拙者は機械学習をPythonではじめる機械学習という本で勉強したので、この本に書いてあるskicit-learnのデータセットの読み出し方がしっくりくる。なのでデータセットを以下のように読み出せるようにプログラムを書いた。
from MyDataSet import beautiful_women
women = beautiful_women.load_beautiful_women()
##プログラム
今回私は画像から顔を切り抜いた段階で画像サイズを128*128に統一していたが、サイズを統一していない場合は、2回目のfor文のコメントアウトしてある部分を外してください。
from PIL import Image
import numpy as np
import os, glob
#画像フォルダへの絶対パス
ROOT_DIR = "/Users/tatsuro64/BeautifulWomenCLF/MyDataSet/imgs/"
#各美女の顔画像フォルダ名
WOMEN = ["aoi_ROI", "erika_ROI", "gakky_ROI", "hikari_ROI", "tsubasa_ROI"]
#美女クラス
class BeautifulWomen:
def __init__(self, data, target, target_names, images):
self.data = data
self.target = target
self.target_names = target_names
self.images = images
#キー(インスタンス変数)を取得するメソッド
def keys(self):
print("[data, target, target_names, images]")
#画像データをNumpy形式に変換してデータセットを作成
def load_beautiful_women():
data = [] #画像の一次元データを格納するlist
target = [] #ラベル(正解)の情報を格納するlist
target_names = ["aoi", "erika", "gakky", "hikari", "tsubasa"]
images = [] #画像の2次元データを格納するlist
for label, woman in enumerate(WOMEN):
file_dir = ROOT_DIR + woman
#美女画像を全て取ってくる
files = glob.glob(file_dir + "/*.jpg")
print("~~~~~~~~{}の画像をNumpy形式に変換し、Listに格納中~~~~~~~~".format(woman))
for i, f in enumerate(files):
img = Image.open(f)
img = img.convert('L') #画像をグレースケールに変換
#img = img.resize((128, 128)) #画像サイズの変更
imgdata = np.asarray(img) #Numpy配列に変換
images.append(imgdata) #画像データ: 128*128の2次元配列
data.append(imgdata.flatten()) #画像データ: 16,384の1次元配列
target.append(label) #正解ラベルを格納
print("------------ListをNumpy形式に変換中--------------")
data = np.array(data)
target = np.array(target)
target_names = np.array(target_names)
images = np.array(images)
#インスタンスを生成
beautifulWomen = BeautifulWomen(data, target, target_names, images)
return beautifulWomen
##実際に読み出してみる
from MyDataSet import beautiful_women
women = beautiful_women.load_beautiful_women()
print(women.data.shape)
print(women.keys())
>>> 実行結果
>>> ~~~~~~~~aoi_ROIの画像をNumpy形式に変換し、Listに格納中~~~~~~~~
>>> ~~~~~~~~erika_ROIの画像をNumpy形式に変換し、Listに格納中~~~~~~~~
>>> ~~~~~~~~gakky_ROIの画像をNumpy形式に変換し、Listに格納中~~~~~~~~
>>> ~~~~~~~~hikari_ROIの画像をNumpy形式に変換し、Listに格納中~~~~~~~~
>>> ~~~~~~~~tsubasa_ROIの画像をNumpy形式に変換し、Listに格納中~~~~~~~~
>>> ----------------ListをNumpy形式に変換中------------------
>>> (1456, 16384)
>>> [data, target, target_names, images]
##この方法によるメリット・デメリット
pickle漬けで保存する場合は、生成したオブジェクトをバイト列にして保存する。なのでデータセットのNumpy形式に変換するのは一度だけでよく、呼び出し時にはバイト列->オブジェクトの変換をするだけで良い。
一方拙者が選んだ上記の方法は呼び出しごとに、データをNumpy形式に変換してデータセットを生成する必要がある。よって計算コストはpickle漬けに比べて高い。しかし、毎回データセットを画像フォルダから生成するため、後からの画像追加などに対応することが出来てより柔軟である。