google colabでの学習について

#概要

google colabはびっくりする位便利ですが、google driveから直接データを読み込むと異様に遅いという問題が存在します。(私の場合画像データ)
というわけで、.p ファイルやら .h5 ファイルやらを使って読み込みを高速化(普通にデバイスから読むのと同じくらいの速度にする)をしようと思います。

*各バイナリファイルがどのようなものかは特に説明しないので、知りたい方は他のページを参照してください。

#全体の流れ

人によってやり方は変わると思いますが、私の場合

1.　データのパスをまとめた .txtファイルをtraining,validation,test用にそれぞれ作成する
2.　1.で作成した.txtファイルを .pファイルに入れる(たぶんここはそんなに早くならない)
3. 先ほど作成した.pファイルを用いて名前とデータ(今回は.png)を対応させた.h5ファイルを作成する

という感じで作ります。

私がいま取り組んでいるタスク(音声合成)の影響でoutputも仰々しく書いていますが、分類タスク等ならわざわざinputとoutputを分けずにタプルで保存すればいいと思います。

#1. .txtファイルの作成


```maketxt.py
import re
import glob
from sklearn.model_selection import train_test_split

def Create_txt(txt_path):
    #動画像用なだけなので、条件はisfile=trueでいいと思います。
    fileList = [p for p in glob.glob('./image/**/', recursive=True) if re.search('/segment_', p)]
    #データは8:1:1
    train_data, val_test_data = train_test_split(fileList, test_size=0.2)
    val_data, test_data =  train_test_split(val_test_data, test_size=0.5)
 
    try:
        train_txt = os.path.join(txt_path, 'train.txt')
        with open(train_txt, mode='x') as f:
            for train_path in train_data:
                f.write(train_path.rstrip('/') + '\n')

        val_txt = os.path.join(txt_path, 'val.txt')
        with open(val_txt, mode='x') as f:
            for val_path in val_data:
                f.write(val_path.rstrip('/') + '\n')

        test_txt = os.path.join(txt_path, 'test.txt')
        with open(test_txt, mode='x') as f:
            for test_path in test_data:
                f.write(test_path.rstrip('/') + '\n')

    except FileExistsError:
        print('already exists')
```

やっていることは、
1.ディレクトリ内のデータのすべてのパスを取得
2.取得したパスを分割
3.それぞれ保存する(try文は上書きしないように書いてあります。)
です。

#2. .pファイルの作成
もともと.txtファイルはそんなに時間かからないので飛ばしても大丈夫です。

pはpickleの頭文字です。オブジェクトを直列化することでオブジェクトの状態を保存することのできるPythonモジュールになっています。(具体的な中身の話はほかのページを見ていただけるとありがたいです。)

今回の場合、大量の文字列(ファイル名のパス)を一つのファイルに格納します。コードは下に置きますが、テキストファイルを読み込んでpickleファイルに入れるだけです。

```
import pickle

video_list =list()
txt_path = os.path.join(txt_path)+ '.txt'
with open(txt_path, 'r') as textFile:
    for line in textFile:
        line = line.replace('\n', '')
        video_list.append(line)

pickle.dump(data_dict, open('txt_file.p', "wb"))
```


#3. .h5ファイルの作成
ここが今回のメインです。

.h5ファイルは、[HDF5](https://www.hdfgroup.org/solutions/hdf5/)と呼ばれるバイナリファイルの一つで、一つのファイルの中で階層構造を持たせることができます。つまり普段コンピュータ内で行っているファイル管理を一つの巨大なファイル内で行おうということです。

大量の画像ファイルを一つの巨大なファイルに格納することで、画像を読み込む時間を削減します。特にgoogle colabは遅いのでこれがないときつかったです。(1エポック目が特にきつく、2時間かかっていたのが10分になりました。)

##データの書き込み
```
fileName = 'data.h5'
path = ('./data_file')
#pickleデータを読み込む
dataFileList = pickle.load(open('txt_file.p', "rb"))
train_list=dataFileList['train']

count =0
with h5py.File(fileName, "w") as f:
  f.create_group('/train')
  f.create_group('/train/input')
  f.create_group('/train/output')
  
  for train in train_list:
    data = pull_item(train)
    f.create_dataset('/train'+data[0].strip('.'), data=data[2])
    f.create_dataset('/train'+data[1].strip('.'), data=data[3])
    f.flush()
    if count %100 ==0:
      print(count)
    count +=1
```

流れとしては、

1. h5ファイルを作成し、input用のディレクトリとoutput用のディレクトリを作成します。(ディレクトリ構造は自由ですが、あまり階層が連なりすぎると読み込みが大変らしいです。)

2. トレーニングデータをどんどん保存していきます。
3. 
```
f.create_dataset('ファイル名', data=データの中身)
```
のようにします。

*コード内のpull_itemには、[inputのファイル名,outputのファイル名,inputの中身,outputの中身]　のようにデータが格納されています。

##データの読み込み
```
import h5py
from PIL import Image
import numpy as np
image = ('/train/input/images_18')
output = ('/train/output/images_18.csv')
with h5py.File('data.h5', mode = 'r') as data:
    img_data = data[image]
    img_group = img_data[...]
    img_group = img_group.astype(np.float64)
    feature = data[output]
    feature_data  = feature[...]
    data.close()
```

仕様上、初めにデータを読み込む際にはHDF5用のオブジェクトが読み込まれるので、

```
img_group = img_data[...]
```
のようにオブジェクトの中身を取り出すことを明示する必要があります。あとはいつも通りです。

#終わりに

"google colab 読み込み　遅い"　等で検索しても、バイナリファイルを使おうといった記事が出てこなかったので、この記事を見て存在を知っていただけると嬉しいです。

調べてみると思ったより奥が深そうですが、私自身はあまり時間がなくちゃんと調べるモチベもないので、より詳しく知りたい方はほかのページものぞいてみるといいと思います。