データ管理にgit lfs を安全に使うためのヒント

データのバージョン管理にgit lfs を使っている人はいるだろう。


### git lfs を使う理由：
- バックアップコピーは、バージョン管理の代わりにならない。
- cloud のファイルサービスは、多数のファイルを扱う際に不便極まりない。
- LFSなしに利用するgit自体は、すぐ膨大なディスクスペースを浪費してしまうので、git lfs を使わずにはいられない。

### git lfsならば
- データのバージョン管理ができる。
- コマンドラインの操作で、データの登録・データの取り出しができる。
- git lfs を利用すれば、git それ自体にある制限を超えて、より規模の大きなデータを扱うことができる。

### この文章で述べたいこと
- 機械学習屋にとって、データの管理は不可欠であるのに、データ管理についてのノウハウはなかなか共有されていない。
- どのような方針でデータ管理するかで、データ管理が楽にも苦痛にもなる。
- その苦痛を減らすために、自分自身の経験から、すべきこととすべきじゃないこととを述べたい。
- 以下の区別は、私のデータ管理のための区別です。人・チームによって別の考え方があるでしょう。
### この文章では述べていないこと
- 機械学習環境のデータ管理をも含めた各種商用サービス。
- いつの実行結果には、どのどういうデータを使っていたのかトレーサビリティを確保する方法。


## 成果物のデータのリポジトリと素材のデータのリポジトリを区別しよう
### 成果物のデータのリポジトリ
- 成果物のデータのリポジトリは共有するためのもの。
- 成果物のデータのリポジトリには、余分なものを含めてはならない。

### 素材のデータのリポジトリ
- 素材データのリポジトリは、取得したデータを成果物にするまでの作業用のもの
- 素材データは、成果物の利用者にとっては、知る必要のないもの。 
- 素材データは、取得したデータを成果物データとするまでの一連の操作で加工される。
- 素材データから成果物データができあがったときには、成果物データのリポジトリにデータを移行し、素材データのリポジトリから削除する。
    - そうすることの利点：未加工の素材データが残っていることがなくなるので、未処理のものと、処理済みのものとを混同することがなくなる。
- 素材データの加工例：
    - 例：画像の一部領域を塗りつぶし
    - 例：画像の輝度のヒストグラムの加工
    - 例：アノテーションの付与 
- このような加工をする際に、バージョン管理しながら作業すれば、間違えた操作をしてしまった際に、古いデータに戻ることができる。
- 素材データを成果物に加工するための作業を複数のメンバーで分業する際には、gitが便利である。
    - 各担当者は作業を開始する前に、最新の状況をgit pullする。
    - 各担当者は作業を終えたあとにgit commit, git pushする。
    - そうすれば、各担当者の作業の結果を反映しやすい。
    - 各担当者は、素材のデータのリポジトリのうちどのフォルダのデータを処理するのか、分業を明確にしておけば、同じフォルダを操作してconflictを生じることは避けられる。

### 成果物のリポジトリを設計する上でのアドバイス
- 成果物の利用者側には、簡単な操作で最新に更新できるようにする。
- train用 valid 用のデータの区別が付きやすいフォルダ構成にする。
- シーン分類などデータの特徴によって、結果を集計・解析しやすいフォルダ構成にする。
- 末端のフォルダでの画像ファイルの数を1000枚を超えないようにする。
    - 利用しているOSのフォルダ表示でサムネール画像の表示しやすいようにしておく。
- オリジナルの画像に対して、ファイルの命名規則は、別の画像に同じファイル名がつかないものにしておく。
- git submoduleを使う場合であっても、submodule を入れ子にしないこと。
- データを追加する際のルールを利用者と合意をとっておくこと。
    - 選択肢：期間を区切る・大規模なデータを追加する際にはフォルダやリポジトリを別にする。
    - 選択肢：シーン分類が重要であって、シーン分類をそろえたいときには、以前のフォルダそのものにデータを追加するという方式もある。この場合、gitのtagをつけておくこと、branch を別にするなどの方法もある。
        - 私のおすすめは、リポジトリを別にする方法。そうすれば、一度十分に確認が済んだリポジトリに、確認が不十分なデータを紛れ込ますことがなくなる。そのため確定したものを確定したものとして扱いやすい。

### 素材データのリポジトリ（＝作業用のリポジトリ）と成果物のリポジトリ
#### 原則　データの加工は作業用のリポジトリで行う。
#### 原則　成果物リポジトリに登録したときには、作業用リポジトリから削除する。
理由：　作業が済んだデータを再度、加工するという無駄を発生させないため。
１つのデータは、作業が完了していないものか、成果物になったものかのどちらかにしか、データあってはいけない。
#### 原則 素材データのリポジトリは、入手時期で区切って別のリポジトリにする。
- 素材データの場合は、成果物のデータの並べかたと同じである必要はない。
- データの加工をして処理を完了したことがわかるようにするためには、入手時期で区切るのが一番明確である。
- いったん、入手して登録して、データの加工に着手したものに、新規のデータが加わらないので、その分扱いが単純になる。

#### 原則 単独のリポジトリの大きさを２GB前後を目安に制限する。
- 巨大すぎるリポジトリは扱いにくい。
- 大きなリポジトリだと複数人で作業をしたときにconflictを生じやすいが、小さなリポジトリで、別々のリポジトリで作業をすれば、conflictが生じにくい。

#### 作業用のリポジトリでバージョン管理しつつ、アノテーションなどの作業を行う。
理由：作業中にデータを破壊してしまうことは、意外とやりがち。
理由：データの加工作業（データの選択、アノテーションの実施、元画像の加工、一部領域の塗りつぶしなど）を複数人で作業をするとき、バージョン管理なしに作業をすると、トラブルは発生しやすくなる。
理由：masterと差分のないローカルのリポジトリは、ローカルに置かなくてよくなる。

### .gitignore ファイルを書く。git lfs track の設定をする。
− リポジトリを作ったときに、必ず上記の設定をする。LFSを設定せずにリポジトリにcommitすると、無駄にリポジトリのサイズを大きくしてしまう。gitサービスが提供している限界に達しやすくなってしまう。
 

### 画像ファイルが壊れていないことをgit add, git commit前に確認する。
- 画像ファイルが、LFSでアクセスするためのリンクに置き換わっていないことを確認する。

- 特に、リポジトリをまとめてpush する以下のコマンドを実行する前には、画像ファイルがきちんと読めるファイルであることを確認しておくのが、強く推奨する。
- git pullでリンクだけをとってくる状態
    - 取得したファイルは、リンク先を示したテキストファイルになっている。
    - 当然、.jpgの拡張子でも、画像として読むことができない。
    - `git lfs install --skip-smudge` を実行した後の状態
- git pull で自動で git lfs pullの操作をする状態
    - `git pull` を実行させると、リンク先の実体を取得する状態
    - jpgの拡張子のファイルを画像として読むことができる。
    - `git lfs install --force` を実行した後の状態

- いちいち画像ファイルを開かずに、コマンドラインで確認する方法
    - fileコマンドの実行でファイルの種類を確認する。　ファイル形式のマーカーだけの確認
    - `file *.jpg` 
    - jpegファイルではないのに、jpgの拡張子の別形式の画像ファイルが紛れ込んでいる場合がある。webから収集した場合。
    - そのようなファイルが見つかった場合には、読める場合には、本当のjpegファイルになるように事前に変換しておく。

### データの加工、データの利用をする前の確認
大量のデータを使う機械学習の分野では、用意した（された）データが問題なく利用できるようにするためにはたくさんの確認を必要とします。
そのような確認を怠ると、たくさん処理をした後になって、壊れたファイルに出くわして、プログラムが異常終了します。問題を生じた壊れたファイルを修復して（あるいは、データをスキップして）処理を初めからやり直しても、次は別のファイルで、壊れたファイルに出くわすことになります。
　ですから、学習プログラムを動かす前に、データのチェックを自動化することです。


- 目的の作業で使用する全てのデータのリポジトリ(=LFSを使っている全てのリポジトリ）でgit lfs pull してあるか確認すること
    - 該当のローカルのリポジトリで、`git lfs pull`を全部に対して実行すること。
- 画像ファイルである場合には、ファイルのマーカーが、該当のファイル形式になっているかをチェックする。
    - ファイルの冒頭のマーカーを読むだけで済むので、短い時間で全数チェックができる。　

### git は使い方を間違えると、作業が地獄になります。
- 検証済みのデータがあるリポジトリには、検証が済んだデータしか登録しない。
- 検証が済むまでの作業用のリポジトリでデータの加工をする。
    - 例：画像データの加工、画像データへのアノテーションは、作業用のリポジトリで行う。
- データの検証が済んだ段階まで、作業用のリポジトリで登録をする。
- その後で、作業用のリポジトリから、検証済みのデータのあるリポジトリにデータを移動する。
    - 一方では、リポジトリからの削除
    - もう一方では、リポジトリへのデータの追加
 


----
# やっちゃいけないこと

## submodule との組み合わせで増える間違いやすさ
### やっちゃいけないこと： submodule を入れ子にする。
- submodule を入れ子にする。そうすると、間違いが飛躍的に増加します。入れ子になった先のsubmodule を展開しないままになってしまうというヘマをやりがちです。
- 対策：入れ子にしてしまっている場合、フォルダ構成を変えて、入れ子を解消しましょう。

#### やっちゃいけないこと: 修復不可能な改変をする際に、十分にチェックしていないデータで上書きのコミットをpush すること

- git の履歴を上書き・削除する関係の作業は、壊れたファイルが混入した状況で作業をすると、回復不可能な状況に陥ります。

LFSを使っている状態だと、some_repo/some_folder/sample.jpg のようなファイルがあっても、目的の画像ファイルそのものではなく、LFSにアクセスするためのhashの情報を含んだファイルにすぎないことがある。

--- 
## Git ならば GCP などの環境での git clone でデータを取得（そして同期）させることが簡単だ。

NAS でのデータ管理だと、NASのデータを GCP にコピーすることは簡単じゃない。