エイバースの中の人

アプリとWEBサービスを作っているABARSのBLOGです。

2019年02月

KerasでHDF5Matrixを使用して学習を高速化

小さな画像が大量にある場合、ImageDataGeneratorを使用するとディスクのランダムアクセスネックでGPU使用率が伸びず、学習速度が低下します。学習画像を事前にHDF5に書き込んでおくことで、ランダムアクセスを抑制し、高速化することができます。

まず、flow_from_directoryから取得できる学習用データからh5pyを使用してHDF5を作成します。以下の例ではtrain.h5を作成します。同様にvalidation.h5も作成します。

train_generator = train_datagen.flow_from_directory(
   INPUT_PATH+'/annotations/'+ANNOTATIONS+'/train',
   target_size=(IMAGE_SIZE, IMAGE_SIZE),
   batch_size=BATCH_SIZE,
   class_mode='categorical',
   shuffle=True
)

training_data_n = len(train_generator.filenames)
training_class_n=len(train_generator.class_indices)

import h5py
f = h5py.File("train.h5", 'w')
train_x = f.create_dataset('training_x', (training_data_n,IMAGE_SIZE,IMAGE_SIZE,3), dtype='f')
train_y = f.create_dataset('training_y', (training_data_n,training_class_n), dtype='f')

cnt=0
for x_batch, y_batch in train_generator:
  for i in range(BATCH_SIZE):
    train_x[cnt] = x_batch[i]
    train_y[cnt] = y_batch[i]
    cnt = cnt+1
    if cnt>=training_data_n:
        break
  if cnt>=training_data_n:
    break

f.close()

学習時はHDF5Matrixを使用します。

  from keras.utils.io_utils import HDF5Matrix
  x_train = HDF5Matrix("train.h5", 'training_x')
  y_train = HDF5Matrix("train.h5", 'training_y')
  x_validation = HDF5Matrix("validation.h5", 'validation_x')
  y_validation = HDF5Matrix("validation.h5", 'validation_y')
  fit = model.fit(
    epochs=EPOCS,
    x=x_train, 
    y=y_train,
    validation_data=(x_validation,y_validation),
    batch_size=BATCH_SIZE,
    shuffle='batch'
  )

VGGFace2を使用した性別推定モデル(3146003画像)では160GBのHDF5を生成し、学習時間を1EPOCで3時間から30分に削減でき、6倍高速化できました。評価環境はGeforceRTX2080、32GBメモリ、1TB SSDです。

floatではなく8bit unsigned intでhdf5ファイルに書き込むこともできるようですが、以下のコードで試してみたところ、学習がほとんど進まなくなってしまいました。

train_x = f.create_dataset('training_x', (training_data_n,IMAGE_SIZE,IMAGE_SIZE,3), dtype='uint8')
train_x[cnt] = x_batch[i].astype(np.uint8)

学習時のノーマライズは下記のように行いましたが、どこかに問題があるようです。

ds_norm = lambda x: x / 255.0
x_train = HDF5Matrix(HDF5_PATH, 'training_x', normalizer=ds_norm)

KerasでGPUのメモリ使用量を抑制する

kerasではデフォルトでGPUメモリを100%確保します。ディスクネックでGPU使用率が低く、複数の学習を同時に走らせたい場合は、モデル作成前に以下のようにすることで、GPUのメモリ使用率を抑制することができます。

import tensorflow as tf
import keras.backend as backend

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.5
config.gpu_options.allow_growth = True
sess = tf.Session(config=config)
backend.set_session(sess)

keras2caffeでKeyErrorが起きる

Caffe 1.0.274でkeras2caffeを使った際、KeyErrorが発生します。

Traceback (most recent call last):
  File "convert_to_caffemodel.py", line 76, in 
    keras2caffe.convert(keras_model, PROTOTXT, WEIGHTS)
  File "keras2caffe\convert.py", line 76, in convert
    if layer_type=='InputLayer' or not hasattr(caffe_net, 'data'):
  File "Anaconda3\envs\py35\lib\site-packages\caffe\net_spec.py", line 180, in __getattr__
    return self.tops[name]
KeyError: 'data'

これは、hasattrがgetattrで実装されていますが、caffeがKeyErrorをAttributeErrorに置き換えていないために発生します。

How to make a class which has __getattr__ properly pickable?

class NetSpec(object):
    """A NetSpec contains a set of Tops (assigned directly as attributes).
    Calling NetSpec.to_proto generates a NetParameter containing all of the
    layers needed to produce all of the assigned Tops, using the assigned
    names."""

    def __init__(self):
        super(NetSpec, self).__setattr__('tops', OrderedDict())

    def __setattr__(self, name, value):
        self.tops[name] = value

    def __getattr__(self, name):
        return self.tops[name]

そこで、NetSpecでKeyErrorをAttributeErrorに変換します。

class NetSpec(object):
    """A NetSpec contains a set of Tops (assigned directly as attributes).
    Calling NetSpec.to_proto generates a NetParameter containing all of the
    layers needed to produce all of the assigned Tops, using the assigned
    names."""

    def __init__(self):
        super(NetSpec, self).__setattr__('tops', OrderedDict())

    def __setattr__(self, name, value):
        self.tops[name] = value

    def __getattr__(self, name):
        try:
            return self.tops[name]
        except KeyError:
            raise AttributeError(name)

これで正常にcaffemodelに変換することができます。

MacOS Mojaveで英語キーボードとして認識される

MacにChromeRemoteDesktopでログインしたあと、JISキーボードが英語キーボードとして認識される問題が起きました。この問題は再起動しても解決しませんでした。

Mojave以前の場合は「キーボードの種類を変更ボタン」がキーボード設定にあったのですが、Mojaveではなくなっているため、以下からキーボード設定アシスタントを実行する必要があります。

/system/Library/CoreService

キーボード設定アシスタントの指示に従うと、JISキーボードとして認識されました。

RTX2080でTensorflow-gpu

機械学習用にRTX2080を搭載したTridentXを購入したので、KerasとTensorflowをインストールしました。



RTX2080の性能を発揮するにはCUDA10が必要ですが、公式のtensorflow-gpuはPython3.5 + CUDA9でビルドされています。そのため、CUDA10を使用してビルドされたtensorflowを別途、入手する必要があります。

ビルド済みバイナリは以下に公開されています。

https://github.com/fo40225/tensorflow-windows-wheel

Anaconda(Python3.7)で以下のようにインストールします。

pip install https://github.com/fo40225/tensorflow-windows-wheel/raw/master/1.12.0/py37/GPU/cuda100cudnn73sse2/tensorflow_gpu-1.12.0-cp37-cp37m-win_amd64.whl


CUDAのランタイムとcuDNNは以下からインストールできます。

CUDA Toolkit 10.0
cuDNN for CUDA10

また、最新のKeras 2.2.4で185万枚の画像からトレーニングしようとした場合、1050tiで1EPOCHで45分だったのが、2080の1EPOCHで50時間必要になってしまい、CUDA10の問題かと思ったのですが、pip install Keras==2.1.4で2.1.4にダウングレードしたら2080で1EPOCHが15分になりました。データがメモリに乗る2週目からは7分程度で1EPOCHが回ります。トータルで6倍程度の高速化のイメージです。
Search
Profile

abars

アプリとWEBサービスを開発しています。最近はUnityとGAE/pyが主戦場。

ブラウザ向けMMOのメトセライズデストラクタ、イラストSNSのイラストブック、東証の適時開示情報を検索できるTDnetSearchを開発しています。

かつてエンターブレインのTECH Win誌でATULADOを連載しました。

サイト:ABARS
Twitter:abars
Github:abars

Twitter
TopHatenar
HotEntry
Counter

アクセス解析付きカウンター。あなたのBLOGにもどうですか?登録はこちらから。

TOP/ BLOG/ LECTURE/ ONLINE/ RUINA/ ADDON/ THREAD/ METHUSELAYZE/ IPHONE/ MET_IPHONE/ ENGLISH/ RANKING