Pythonでデータセットを作る方法とデータセットの中身を確認する方法(npz編)

2020年4月28日

今回は拡張子が npz のデータセットの作り方を説明します。

また、作成したデータセットの中身を確認する方法も説明します。

今回作成するデータセットは、画像とラベルをセットにしたファイルになります。

このデータセットは、主にDeep Learning で物体分類や物体検出の学習をさせる際に使用したり、主成分分析(PCA:Principal component analysis)や多様体学習の等尺性マッピング(Isomap:isometric mapping)を行う際に使用します。

データセットは、作り方を知っていれば簡単に作れますが、知らないと作れません。

機械学習を始めたばかりの方にとっては、そもそもデータセットってなに?という感じだと思います。

一度作り方を覚えれば簡単に作れるようになります。

色々と応用できるので覚えておいて損はないです。

まずはソースコードをマネして自分で実行してみてください。

理解が進むと思います。

ソースコード(npz編)

以下のソースコードを見てください。

# read_image_npz.py # プログラム名

import cv2
import glob
import random
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

def read_image():

    # 設定パラメータ
    class_n = 2   # クラス数
    x = []        # 画像データ用
    y = []        # ラベルデータ用

    # ファイルの保存先を指定(ファイルの拡張子は.npz)
    outfile ="./photo/" + str("224px_") + str(class_n) +  ".npz"

    # path 以下の画像を読み込む
    def glob_files(path, label):
        files = glob.glob(path + "/*.jpg")  # pathにある画像を読み込む
        random.shuffle(files)               # ランダムに画像を読み込む 

        # 各ファイルを処理
        num = 0
        for f in files:
            if num >= len(files): break
            num += 1
            # 画像ファイルを読む
            img = Image.open(f)    # Pillow(PIL)で画像読込み。色順番はRGB
            img = np.asarray(img)  # ndarray化
            img = cv2.resize(img, (224, 224), cv2.INTER_LANCZOS4)  # 画像サイズを224px × 224pxにする
            
            # 画像データ(img)とラベルデータ(label)をx, y のそれぞれのリストに保存
            x.append(img)
            y.append(label)

    i = 0
    for i in range(class_n):
        glob_files("./photo/" + str(i), i)  # 各画像のフォルダーを読む
        print("file: " + str(i))

    ### ファイルへ保存 ###
    # npzで作成する場合
    np.savez(outfile, x=x, y=y)
    print("npzファイルを保存しました :" + outfile, len(x))

if __name__ == '__main__':
    read_image()

ポイント

1.npz ファイルを保存する際は、np.savez( ) を使用する

これは決まり事です。

NumPyを使うことで画像の配列(ndarray)をそのまま保存できます。

以下のコードについて説明します。

np.savez(outfile, x=x, y=y)

カッコの中の第一引数は、npz ファイルの保存先のパスを示しています。

また、第二引数の x は画像データを、第三引数の y はラベルデータを示しています。

x の画像とy のラベルデータは、この順番で保存されます。

2.画像をランダムに読み込む際は、random.shuffle( ) を使用する

データセットをDeep Learning の学習に使うのであれば必須です。

なぜなら、画像データを収集した際に無意識に人間の思いが入り、偏ったデータセットになってしまう可能性があるからです。

データセットが偏ったデータで構成されていると、Deep Learning で学習させた際、偏ったモデルができます。

偏ったモデルを使って推論すると、偏った推論をしてしまう可能性があります。

これを避けるためにシャッフルします。

シャッフルすることで、偏ったデータセットになることを避けることができます。

必要がなければ、ソースコードの先頭に# を付けてコメントアウトしてください。

コメントアウトすればこのソースコードは実行されません。

3.ソースコードを def で関数化しておく

一番最後のソースコードを見てください。

if __name__ == '__main__':
    read_image()

これは、プログラムが実行された際、一番最初に read_image( ) が実行されるという意味です。

つまり、def read_image( ) の中身が実行されます。

中身とは def read_image( ): 以降のインデント(字下げ)されている部分です。

上記ソースコードでは、def の中にさらに def が入っています。

基本的には、一つの関数内では一つのタスク(仕事)しかさせない方が良いです。

なぜなら、プログラムは可能な限りシンプルにした方が良いからです。

シンプルな方が間違いに気づきやすいし修正も容易です。

また、ソースコードの他の部分への影響を最小限に抑えることもできます。

このため以下のように仕事を分担させています。

・内側の def glob_files( ) の仕事

画像をフォルダから読み込んで x と y のリストに保存する。

・外側の def read_image( ) の仕事

 x と y のリストが完成した後、np.savez(outfile, x=x, y=y) で 所定のフォルダに npz ファイルを保存する。

実行結果

上記プログラムを実行してみます。

まず、上記ソースコードをread_image_npz.py という名前で好きなフォルダ内に保存してください。

次に、上記のプログラムファイルが置いてある同じフォルダ内に、photo というフォルダを作ってください。

さらにそのphotoフォルダ内に「0」と「1」という名前のフォルダを作ってください。

この「0」と「1」フォルダ内に画像をとりあえず各10枚くらい保存してください。

なお、今回のプログラムでは写真はリサイズされます。

理由は計算負荷を小さくするためと npz ファイルのサイズを小さくしたいからです。

リサイズが不要であれば、img = cv2.resize( ) の先頭に # を付けてコメントアウトしてください。

リサイズする場合は、画像は正方形にトリミングしておくことをおすすめします。

理由は、画像に映っている像が変形してしまうためです。

写真サイズは 224px × 224px にするか、それよりも多少大きければよいです。

最近のデジタルカメラで写真を撮ると、3000px × 2000px サイズ等で撮影できますが、サイズが大きいと計算負荷が大きく処理に時間が掛かります。

また、npz ファイルのサイズも数百MB、数GBになる可能性があります。

今回は参考プログラムなので画像サイズは小さくしています。

プログラムを実行すると以下のファイルが作成されます。

224px_2.npz

このファイルは、各画像に「0」というラベルと「1」というラベルが付いたデータになります。

次に、この npz ファイルの中身を確認してみます。

データセットの中身を確認する方法

以下のソースコードを見てください。

# image_plot.py #  プログラム名

import numpy as np
import matplotlib.pyplot as plt

# 設定パラメータ
img_n = 9

### npz用 ###
### データセットをnpzで作成した場合 ###
photos = np.load('./photo/224px_2.npz')  # 写真データを読み込み
x = photos['x']
y = photos['y']

# 開始インデックス(idx = 0 一番最初の画像)
idx = 0

# 画像表示
plt.figure(figsize=(10, 10))
for i in range(img_n):
    plt.subplot(3, 3, i+1)  # 3行3列の1番目から9番目まで表示
    plt.axis('off')         # 画像にスケールを入れない
    plt.title(y[i + idx])   # タイトルにインデックスを表示
    plt.imshow(x[i + idx])  # 画像がカラー(RGB)の場合
  #plt.imshow(x[i + idx], cmap='gray')  # 画像がグレーの場合
plt.show()

ポイント

1.npz ファイルを読み込む際は、np.load( ) を使用する

ファイルを読み込む際も NumPy を使います。

np.load( ) のカッコの中に npz ファイルが入ったフォルダのパスを記載します。

なお、パスを記載する際は必ず ’ ’ で囲ってください。囲わないとエラーになります。

2.画像データとラベルデータを扱いやすくするため分離する

以下のソースコードで 画像 photos[ ' x ' ] と ラベルデータ photos[ ' y ' ]を x と y の変数にそれぞれ代入しておきます。

x = photos[ ' x ' ]
y = photos[ ' y ' ]

理由は、画像データとラベルデータを扱いやすくするためです。

3.画像表示は matplotlib の plt を使用する

おなじみの matplotlib を使用します。

ちなみに、グレー画像を表示させる場合は plt.imshow( ) のカッコ内に cmap=’gray’ を追記すればOKです。

カラー画像(RGB)を表示させる場合は cmap は不要です。

表示結果

ラベル 0
ラベル 1

各写真にラベル 0 とラベル 1 がタイトルとして付与されていることが分かります。

npz ファイルに写真とラベルがセットで保存されていることが確認できました。

まとめ

  • npz ファイルを保存する際は、np.savez( ) を使用する
  • 画像をランダムに読み込む際は、random.shuffle( ) を使用する
  • 一つの関数内では一つのタスク(仕事)しかさせない方が良い
  • npz ファイルを読み込む際は、np.load( ) を使用する
  • 画像データとラベルデータを扱いやすくするため分離する
  • 画像表示は matplotlib の plt を使用する
  • グレー画像なら plt.imshow( ) のカッコ内に cmap=’gray’ を追記する