shine-Notes

ゆるふわ思考ダンプ

icrawlerで収集したデータをGoogle Cloud Storageに保管し、Google Colabにマウントする

サマリ

  • 自前でCNN系のモデルを訓練するに当たり、任意の画像データを集めてGoogle Colabで処理したい
  • 取ったルートは、ローカル環境でicrawler実行→そのままデータをGoogle Cloud Storageへアップロード→Google Colabでマウントして読み込み
  • あまり手を掛けずに画像データを集めるなら、下手なスクレイピングよりicrawlerが一番はやいが、多少クセあり

モチベーション

サマリの通りで、サンプルデータ等ではなく自前で集めたデータでCNNを作ってみたいと考えた。
事前調査としてスクレイピングの本を今更読んだりもしたのだが、手っ取り早く画像だけを集めたいなら(ScrapyやBeatifulSoup等ではなく)icrawlerが一番手っ取り早いと判断した。

そして集めたデータの活用方法を考えると、そのままGoogle Colabで使えるほうがありがたいよね、という事で、(他にも手段は色々あると思うが)個人的にGoogle CloudのFree Tierを使い倒したく、シンプルにGoogle Cloud Storageへのアップロードを選んだ。 実際に書いたコードは下記の通り。以降はやったときの詰まりどころを書いていく。

github.com

icrawler

紹介記事のとおりなのだが、pip installして、検索名称を与えればチャッチャとデータを集めてきてくれる。めちゃくちゃ楽。

qiita.com

とはいえ既知の問題でもあるが、以下引っかかったポイント。

  • Google画像検索APIは使えない。Bing画像検索を指定して使う。
  • 大量データを集めたい場合、上限に引っかかる。上手いこと検索条件を分けたスレッドを動かさないと1000枚近い画像は集められない。
  • そもそもあたりの悪いキーワードを与えると段々と関係ない画像が混ざってくる。

いずれもググると出てくる既知の内容だが、簡単に触れておこう。

- Google画像検索は使えない

どうやらGoogle画像検索APIの仕様変更にicrawler側が対応できていないため、GoogleCrawlerが利用できない。 github.com

そのため、基本的にはBingやBaiduの画像検索を利用することになる。

from icrawler.builtin import BingImageCrawler

crawler = BingImageCrawler(storage={"root_dir": './'})

- スレッド上限(1000)に引っかかる

Documentにもある通り、スレッド処理件数の上限が1000件となっており、大きめのMAX_NUMを指定してもこの上限に引っかかる。 そしてDocumentにも対策が示されているように、回避するためにはフィルタ上限を重複しないように指定した上で、クローラのスレッドを複数動かすしか無い。(最終的に画像を保存する段階では逐次処理してくれるので、画像の重複は発生しない)ただ、例示されているのは現在利用できないGoogle画像検索のクローラというオチ。Bing画像検索は、実際にWebページを見ると分かるがFromTo検索ができない(直近3ヶ月、みたいな条件しか張れない)ので単純にこの方法を書き換えては使えない。

何種類か試して悩んだ末、「License」で複数種のパラメータ指定ができた。他にもやり方は有ると思うが、自分なりに実装してみた例は記載しておく。

    # multiple threads
    crawler = BingImageCrawler(
        feeder_threads=4,
        parser_threads=4,
        downloader_threads=4,
        storage = {"root_dir": WORKING_DIRECTORY})

    # crawl
    crawler.crawl(
        keyword = SEARCH_WORD, 
        max_num = SEARCH_QT, 
        filters={'license': 'creativecommons'},
        file_idx_offset=0)

    crawler.crawl(
        keyword = SEARCH_WORD, 
        max_num = SEARCH_QT,  
        filters={'license': 'publicdomain'},
        file_idx_offset='auto')

    # 以下略

mystudying-icrawler2gcs/main.py at master · shinebalance/mystudying-icrawler2gcs · GitHub

- そもそも関係ない画像が混ざってくる

これは根本的には対策しようがないのだが、画像が豊富に出てくるようなキーワードを与えないと、ある程度の画像枚数以降から全然関係ない画像が混ざってくるようになる。コツとしては、可能なら英語のキーワードを入れてみる、or条件で引っかかりそうな単語を増やす…くらいか。

Google Cloud Storageへのアップロード

画像が集め終わったら、今度はGoogle Cloud Storageへアップロードする。といっても、だいたい下記サイトのとおりに進めれば実行できる。大枠としては、事前にGoogle Cloud SDKを導入しておいてトークンを発行、Pythonコード内で環境変数として読み込んでおけば、指定したBucketにファイルをアップロードできる。ここは大して苦労しなかったので詳しくは書かない。

sleepless-se.net qiita.com

Google ColabからGCS上のバケットを読み込む

こちらも一回慣れてしまえば簡単。 Google Cloudに対し権限を持っているアカウントでColabを起動し、以下を実行することでワンタイムコードの発行、認証が行える。

from google.colab import auth
auth.authenticate_user()

あとはGoogle Cloud Storage FUSEをColab内でインストールすればよい。

!echo "deb http://packages.cloud.google.com/apt gcsfuse-bionic main" > /etc/apt/sources.list.d/gcsfuse.list
!curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
!apt -qq update
!apt -qq install gcsfuse

stackoverflow.com

1点癖のある挙動がバケット内のディレクトリの認識で、ディレクトリだけをマウントすることはできず、

  1. まずバケットをマウントする
  2. 次にバケット内のディレクトリと同じ名前でmkdirする
  3. あら不思議!作ったディレクトリの中にバケットの中と同じファイルが入っている!

という手順を取る必要がある。

!mkdir imgOnColab
# gcsfuse [バケット名] imgOnColab 
!gcsfuse befree-storage imgOnColab
# バケットの中にnew balance shoes'というディレクトリがある
! mkdir imgOnColab/new\ balance\ shoes
# これで見えるようになる

参考 stackoverflow.com

以上の部分のみ切り出したNotebookが下記の通り。 mystudying-icrawler2gcs/load_image_fromGCS.ipynb at master · shinebalance/mystudying-icrawler2gcs · GitHub

Google Colabが継続的に改善されているため、時期によって実行時のUIは多少変わるかもしれないが、2020年上半期時点では以上の手順で簡単に実行できた。

まとめ

というわけで、最終的にGoogle Colabで自分の集めた画像を扱えるようになった。一旦はこれで必要十分かなと思っているが、以下思ってること。

  • そもそものicrawlerでの処理をCloud Functionあたりに移管できないかな…
  • icrawlerは良くも悪くも何をやってるか理解しなくても使えるので、理解という意味ではもう少しソースコードを読み込みたい

以上