GitHubじゃ!Pythonじゃ!

GitHubからPython関係の優良リポジトリを探したかったのじゃー、でも英語は出来ないから日本語で読むのじゃー、英語社会世知辛いのじゃー

humphd

have-fun-with-machine-learning – 機械学習とニューラルネットワークによる画像分類の絶対初心者ガイド

投稿日:

機械学習とニューラルネットワークによる画像分類の絶対初心者ガイド

機械学習で楽しむ:初心者のためのガイド

中国語(繁体字)でも利用できます。

序文

これは、AIを背景としていないプログラマーの機械学習の手引きです。 ニューラルネットワークを使用するには博士号を必要とせず、今日存在するものを使用するためにAIで次のブレークスルーを行う人である必要はありません。 私たちが今持っているものは、すでに息を呑むほどであり、非常に使いやすいものです。 研究テーマのように扱うのではなく、他のオープンソース技術と同じように、私たちの多くはこのようなもので遊ぶ必要があると私は信じています。

このガイドでは、機械学習を使用して、 データ/訓練されていないサンプルの画像が、画像自体のみを使用してイルカシホアのものかどうかを高い確度で予測するプログラムを作成することになります前。 ここでは、使用する画像の例を2つ示します。

そのために、 畳み込みニューラルネットワーク(CNN)を訓練して使用する予定です。 私たちは、開業医の視点から第一の原則からこれにアプローチします。 現在、AIについては非常に興奮していますが、書かれていることの多くは、公園内の友人の代わりに、物理教授が物理的な教授によってあなたの自転車のトリックを教えるように感じられるように感じられます。

私はこれをGithubとブログ記事の両方に書くことに決めました。なぜなら、私が下に書いたことのいくつかは誤解を招く、素朴な、または単純な間違ったものだと確信しています。 私はまだ自分自身を学んでいるし、初心者のドキュメントが不足していることが障害であることがわかった。 間違いや重要な情報が欠落している場合は、プルリクエストを送信してください。

そのすべてが途中で、あなたの自転車でいくつかのトリックをする方法を教えてください!

概要

ここでは、私たちが探求するものを挙げます:

  • 既存のオープンソースマシン学習技術、特にCaffeDIGITSをセットアップして使用する
  • 画像のデータセットを作成する
  • ニューラルネットワークをゼロから訓練する
  • これまでに見たことのない画像でニューラルネットワークをテストする
  • 既存のニューラルネットワーク(AlexNetとGoogLeNet)を微調整することでニューラルネットワークの精度を向上させる
  • ニューラルネットワークを展開して使用する

このガイドでは、ニューラルネットワークがどのように設計されているか、多くの理論をカバーしているか、単一の数学的表現を使用しているかについては教えていません。 私はあなたに見せたいことのほとんどを理解しているようなふりをしていません。 代わりに、私たちは既存のものを面白い方法で使用して、難しい問題を解決しようとしています。

Q:「ニューラルネットワークの理論については話しませんが、少なくとも私たちが行く前には概観が好きなような気がします。

短い記事から完全なオンラインコースまで、文字通り何百もの紹介があります。 学習方法に応じて、出発点としては以下の3つのオプションがあります。

セットアップ

使用するソフトウェア(CaffeとDIGITS)をインストールすることは、お使いのプラットフォームとOSのバージョンに応じて、イライラする可能性があります。 これを行う最も簡単な方法は、Dockerを使用することです。 以下では、Dockerを使ってそれを行う方法と、それをネイティブに行う方法を調べます。

オプション1a:Caffeをネイティブにインストールする

まず、Berkely Vision and Learning Center(BSDライセンス)のCaffeディープ・ラーニング・フレームワークを使用します。

Q:「ちょっと待って、なぜカフェ? 最近誰もが話しているTensorFlowのようなものを使ってみてはいかがですか… “

利用可能な選択肢はたくさんあり、すべてのオプションを調べる必要があります。 TensorFlowは素晴らしいですし、あなたはそれで遊ぶべきです。 しかし、私はCaffeをいくつかの理由で使用しています。

しかし、私がCaffeを使用している理由の1つは、あなたがそれを扱うための コード書く必要がないということです 宣言的にすべてを行うことができます(Caffeは構造化テキストファイルを使用してネットワークアーキテクチャを定義します)、コマンドラインツールを使用します。 また、Caffeのフロントエンドを使ってトレーニングを行い、ネットワークをより簡単に検証することができます。 私たちはこの目的のために、以下のnVidiaのDIGITSツールを使用します。

Caffeはインストールするにはちょっとした作業になる可能性があります。 DockerやAWSの設定など、さまざまなプラットフォームのインストール手順があります。

注:私のウォークスルーをするときは、GithubのレポからCaffeの次のリリースされていないバージョンを使用しました: https : //github.com/BVLC/caffe/commit/5a201dd960840c319cefd9fa9e2a40d2c76ddd73

Macでは、作業中にさまざまなステップで進行状況が停止するという問題が発生し、作業に迷惑をかけることがあります。 私は数日の試行錯誤を繰り返しました。 少しずつ違った問題があるガイドがたくさんあります。 結局、私はこれが最も近いことを発見しました。 私はまた、 この記事を非常に最近のものであり、私が見たのと同じ議論の多くへのリンクであることをお勧めしたいと思います。

Caffeをインストールすることは、我々がやることの中で最も難しいことです.AIの面がより難しいと想定しているので、かなりきれいです! 問題がある場合はあきらめないでください。痛みを感じる価値があります。 私がこれをやり直していたら、おそらくMacで直接やってみるのではなく、UbuntuのVMを使うだろう。 答えが必要な場合は、 Caffe Usersグループもあります。

Q:「ニューラルネットワークをトレーニングするためには強力なハードウェアが必要ですか? 気になるGPUにアクセスできない場合はどうしたらいいですか?

深いニューラルネットワークでは、最初からトレーニングして大規模なデータセットを使用する場合は、トレーニングに多くのコンピューティングパワーとエネルギーが必要です。 私たちはそれをするつもりはありません。 その秘密は、他の誰かがすでに何百時間もの計算時間トレーニングをして、それをあなたの特定のデータセットに微調整するために、事前に訓練されたネットワークを使用することです。 ここでは、これを行う方法を見ていきますが、私があなたに見せるすべてのことを言えば十分です。私は1年前のMacBook Proで気の利いたGPUなしでやっています。

私は統合IntelグラフィックスカードとnVidia GPUを持っているので、 OpenCL Caffeブランチを使うことにしました。私のラップトップではうまくいきました。

Caffeのインストールが完了したら、次のことをすべてやっている必要があります。

  • あなたのビルドされたcaffeを含むディレクトリ。 これを標準的な方法で行った場合は、caffeやPythonのバインディングなどを実行するために必要なものすべてを含むbuild/ dirがありますCAFFE_ROOT build/を含む親ディレクトリはCAFFE_ROOTなります(後で必要になります) 。
  • make test && make runtest実行しmake test && make runtestを渡す
  • すべてのPython depsをインストールした後(py pip install -r requirements.txt python/ pip install -r requirements.txtpython/で実行しmake pycaffe && make pytest )、 make pycaffe && make pytest実行しmake pycaffe && make pytestください
  • distribute/実行する際には、必要なすべてのヘッダーやバイナリなどを含む、 distribute/なバージョンのcaffeを作成するために、 make distributeも実行make distribute必要があります。

私のマシンでは、Caffeが完全に構築されているので、CAFFE_ROOTディレクトリに次の基本レイアウトがあります。

caffe/
    build/
        python/
        lib/
        tools/
            caffe ← this is our main binary 
    distribute/
        python/
        lib/
        include/
        bin/
        proto/

この時点で、私たちはニューラルネットワークを使って訓練、テスト、プログラムするために必要なものすべてを持っています。 次のセクションでは、ユーザーフレンドリーでウェブベースのフロントエンドをCaffeのDIGITSに追加し、ネットワークのトレーニングとテストをはるかに簡単にします。

オプション1b:ネイティブにDIGITSをインストールする

nVidiaのディープラーニングGPUトレーニングシステム(DIGITS )は、ニューラルネットワークをトレーニングするためのBSDライセンスのPython Webアプリケーションです。 DIGITSがCaffeのコマンドラインやコードで行うことはすべて可能ですが、DIGITSを使用すると簡単に始めることができます。 私はまた、偉大な視覚化、リアルタイムのグラフ、およびその他のグラフィカルな機能のために、より楽しいと感じました。 あなたが実験して学習しようとしているので、私はDIGITSで始めることを強くお勧めします。

いくつかの良いドキュメントがhttps://github.com/NVIDIA/DIGITS/tree/master/docsにあります。いくつかのインストール設定はじめにのページがあります。 私はあなたがDIGITSでできることすべての専門家ではないので、あなたが続行する前にすべてのものを読むことをお勧めします。 質問が必要な場合は、 DIGITSユーザーグループも公開しています。

Linux上でDockerからプリベーク済みのパッケージにDIGITSをインストールして実行する方法はさまざまですが、ソースからビルドすることもできます。 私はMac上にいるので、ソースから構築しました。

注:私のウォークスルーでは、GithubのリポジトリからDIGITSのリリースされていないバージョンを使用しました: https : //github.com/NVIDIA/DIGITS/commit/81be5131821ade454eb47352477015d7c09753d9

それは単なるPythonスクリプトの集まりなので、作業するのはかなり苦労しました。 あなたがしなければならないことの1つは、サーバを起動する前にCAFFE_ROOTが環境変数を設定することによってCAFFE_ROOT指示することです:

export CAFFE_ROOT=/path/to/caffe
./digits-devserver

注:私のPythonバイナリはpython2と呼ばれ、私はpython2.7しか持っていないと仮定して、Mac上で私はサーバスクリプトに問題がありました。 /usr/binシンボリックリンクするか、DIGITSスタートアップスクリプトを修正して、システム上の適切なバイナリを使用することができます。

サーバーが起動したら、 http:// localhost:5000の Webブラウザーで他の操作を行うことができます。これは以下で説明します。

オプション2:ドッカーを使用したCaffeとDIGITS

まだインストールされていない場合はDockerをインストールし、次のコマンドを実行してCaffe + Digitsの完全なコンテナを取得して実行します。 注意すべきいくつかの点:

  • ポート8080が別のプログラムによって割り当てられていないことを確認してください。 その場合は、必要な他のポートに変更してください。
  • /path/to/this/repositoryをこの複製された/path/to/this/repositoryの場所に/data/repoし、コンテナ内の/data/repoはこのディレクトリにバインドされます。 これは、以下で説明する画像にアクセスするのに便利です。
docker run --name digits -d -p 8080:5000 -v /path/to/this/repository:/data/repo kaixhin/digits

コンテナを実行したので、Webブラウザを開き、 http://localhost:8080を開きhttp://localhost:8080 リポジトリ内のすべてがコンテナディレクトリ/data/repoます。 それでおしまい。 あなたはCaffeとDIGITSを使っています。

シェルアクセスが必要な場合は、次のコマンドを使用します。

docker exec -it digits /bin/bash

ニューラルネットワークのトレーニング

ニューラルネットワークをトレーニングするには、いくつかのステップが必要です。

  1. 分類された画像のデータセットを組み立てて準備する
  2. ネットワークのアーキテクチャを定義する
  3. 準備されたデータセットを使用してこのネットワークを訓練し検証する

私たちはこれを3つの方法でやっていきます。最初から始めることと事前にネットワークを使うことの違いを示し、Caffeで一般的に使用されている2つの一般的な事前ネットワーク(AlexNet、GoogLeNet)およびDIGITs。

私たちのトレーニングの試行では、DolphinsとSeahorsesの小さなデータセットを使用します。 私はデータ/イルカ・シーホーに使った画像を入れました。 少なくとも2つのカテゴリが必要ですが、さらに多くのカテゴリを持つことができます(使用するネットワークの一部は1000以上の画像カテゴリで訓練されています)。 私たちの目標は、私たちのネットワークにイメージを与えて、イルカかシホルスかを教えてもらうことです。

データセットの準備

最も簡単な方法は、画像を分類されたディレクトリレイアウトに分割することです。

dolphins-and-seahorses/
    dolphin/
        image_0001.jpg
        image_0002.jpg
        image_0003.jpg
        ...
    seahorse/
        image_0001.jpg
        image_0002.jpg
        image_0003.jpg
        ...

ここで、各ディレクトリは分類したいカテゴリです。そのカテゴリ内の各画像は、トレーニングと検証に使用する例です。

Q:「イメージは同じサイズでなければなりませんか? ファイル名はどういう意味ですか?

両方にはいいよ。 イメージのサイズは、ネットワークに供給する前に正規化されます。 最終的には256×256ピクセルのカラー画像が必要になりますが、DIGITSは画像を自動的に切り詰めたり、スカッシュしたりします。 ファイル名は無関係です。これらのファイルがどのカテゴリに含まれているかは重要です。

Q:「私のカテゴリのより複雑なセグメンテーションはできますか?」

はい。 https://github.com/NVIDIA/DIGITS/blob/digits-4.0/docs/ImageFolderFormat.mdを参照してください

これらのイメージをディスク上で使用して新しいデータセット 、具体的には分類データセットを作成します。

DIGITSが提供するデフォルトを使用し、 data / dolphins-and-seahorsesフォルダのパスにTraining Imagesを指定します。 DIGITSは、カテゴリ( dolphinseahorse )を使用して、スクラッシュされた256 x 256トレーニング(75%)とテスト(25%)画像のデータベースを作成します。

あなたのデータセットに名前とdolphins-and-seahorsesショウジョウバエを与え、 Createをクリックします

これにより私のラップトップでは4秒しかかからなかったデータセットが作成されます。 結局、私は92のトレーニング画像(49ドルフィン、43シオルズ)を2つのカテゴリに持ち、30のバリデーション画像(16頭のイルカ、14頭のシーホース)を持っています。 それは実際には小さなデータセットですが、実験や学習の目的には最適です。なぜなら、それを使用するネットワークを訓練して検証することは永遠に行われないからです。

あなたは彼らが押しつぶされた後に画像を見たい場合は、db探検することができます。

トレーニング:一から試してみよう

DIGITSのホーム画面に戻り、新しい分類モデルを作成する必要があります。

まず、 dolphins-and-seahorsesショウのデータセットを使用するモデルを習得し、DIGITSが提供するデフォルト設定を開始します。 最初のネットワークでは、標準的なネットワークアーキテクチャーの1つ、 AlexNet(pdf)を使用することを選択します。 AlexNetのデザインは、2012年にImageNetと呼ばれる主要なコンピュータビジョンコンペを受賞しました。このコンテストでは、120万画像に1000以上の画像カテゴリを分類する必要がありました。

Caffeは構造化テキストファイルを使用してネットワークアーキテクチャを定義します。 これらのテキストファイルは、 Googleのプロトコルバッファに基づいています Caffeが使用する完全なスキーマを読むことができます。 ほとんどの場合、これらを使用して作業するつもりはありませんが、後の手順で変更する必要があるため、それらの存在を認識することは良いことです。 AlexNetのprototxtファイルは、次のようになります(例: https : //github.com/BVLC/caffe/blob/master/models/bvlc_alexnet/train_val.prototxt)

私たちはネットワークを30エポックに訓練します(つまり、訓練画像で習得し、妥当性検証画像を使用してテストします)。ネットワークの重量を調整し、このプロセスを30回繰り返します。 それがサイクルを完了するたびに、 精度 (0%〜100%、高い方が良い)と弊社の損失 (低い方が良いとされたすべてのミスの合計)に関する情報が得られます。 理想的には、高精度で、エラーの少ない(小さな損失)予測が可能なネットワークが必要です。

注:このトレーニングを行っているDIGITSでは、打撃エラー報告されている人がいます。 多くの人にとって、利用可能なメモリに関連する問題(プロセスは動作するために大量のメモリが必要です)。 Dockerを使用している場合は、DIGITSで使用可能なメモリ量を増やしてみてください(Dockerでは、preferences – > advanced – > memory)。

当初、私たちのネットワークの精度はわずか50%です。 これは当初、ランダムに割り当てられた重みを使用して2つのカテゴリ間で「推測」するためです。 時間の経過とともに、87.5%の精度を達成することができ、損失は0.37になります。 30回のエポック・ランで6分もかかりました。

アップロードした画像やウェブ上の画像のURLを使用してモデルをテストできます。 トレーニング/検証データセットになかったいくつかの例でそれをテストしましょう:

私たちが別のものを試すまで、それは完璧に見えるでしょう:

ここでは完全に落ち込み、イルカのためにシホルスを混乱させます。悪化すると、高い信頼感でそうします。

現実には、私たちのデータセットは小さすぎて本当に良いニューラルネットワークを訓練するのには役に立たないということです。 実際には、10〜100万枚の画像が必要です。それで、すべてを処理するための多くのコンピューティングパワーが必要です。

トレーニング:試行2、微調整AlexNet

ファインチューニングのしくみ

ニューラルネットワークを一から設計し、それを訓練するのに十分なデータ(例えば、何百万もの画像)を収集し、訓練を完了するためにGPUに数週間アクセスすることは、私たちの大部分を超えています。 少量のデータを実用化するために、 Transfer Learning 、またはFine Tuningという手法を採用しています。 ファインチューニングは、ディープニューラルネットワークのレイアウトを利用し、事前に訓練されたネットワークを使用して、初期オブジェクト検出の困難な作業を行います。

ニューラルネットワークを使って、遠く離れた何かを双眼鏡で見るようなものを想像してみてください。 最初に双眼鏡を目にして、すべてがぼやけています。 フォーカスを調整すると、色、線、形が見え始め、最終的には鳥の形を選ぶことができます。その後、鳥の種を特定することができます。

多層ネットワークでは、初期レイヤはフィーチャ(例えばエッジ)を抽出し、後のレイヤはこれらのフィーチャを使用して形状(例えば車輪、目)を検出し、以前の層からの累積された特性(例えば、猫対犬)。 ネットワークは、ピクセルから円、目、特定の方向に配置された2つの目に行くことができなければならず、最終的に画像が猫を描写すると結論づけることができるまででなければならない。

私たちがやりたいことは、既存の訓練されたネットワークを、最初に訓練されたクラスではなく新しいクラスのクラスを分類するために特化することです。 ネットワークは既に画像内の機能を「見る」方法をすでに知っているので、特定の画像タイプを見るために再学習したいと考えています。 大部分のレイヤーから最初から始める必要はありません。これらのレイヤーで既に行われている学習を新しい分類タスクに移行したいと考えています。 ランダムウェイトを使用した以前の試みとは異なり、トレーニングでは最終的なネットワークの既存のウェイトを使用します。 しかし、最終的な分類レイヤーを捨て画像データセットでネットワークを再テストし、イメージクラスに微調整します。

これが機能するためには、学習した重みが有用であるように、私たち自身のデータと十分に似ている事前訓練されたネットワークが必要です。 幸いなことに、以下で使用するネットワークは、 ImageNetの数百万の自然画像で訓練されました 。これは、幅広い分類タスクで役立ちます。

この技術は、医療画像から目の病気をスクリーニングする、海で採取した顕微鏡画像からプランクトン種を特定する、Flickr画像の芸術的スタイルを分類するなどの面白いことを行うのに使用されています。

これを行うには、機械学習のすべてのように、データとネットワークアーキテクチャを理解する必要があります。データの過不足に注意しなければならず、いくつかのレイヤーを修正する必要があり、新しいレイヤーを挿入する必要があります。しかし、私の経験では、時間の大部分は「ちょうど仕事」であり、私たちの素朴なアプローチを使って何が達成できるかを見るための実験を行うだけの価値があります。

事前訓練されたネットワークのアップロード

最初の試みでは、AlexNetのアーキテクチャーを使用しましたが、ネットワークのレイヤーではランダムウェイトから始めました。 私たちがやりたいことは、既に大規模なデータセットで訓練されたAlexNetのバージョンをダウンロードして使用することです。

ありがたいことに、これを正確に行うことができます。 AlexNetのスナップショットはhttps://github.com/BVLC/caffe/tree/master/models/bvlc_alexnetからダウンロードできます 訓練されたウエイトを含むバイナリの.caffemodelファイルが必要です。このファイルはhttp://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodelからダウンロードできます

あらかじめ訓練されたモデルをダウンロードしているときに、もう一度同じことをしましょう。 2014年、GoogleはGoogLeNet (コードネーム:Inception)と同じImageNet競争で勝利しました。これは22層のニューラルネットワークです。 GoogLeNetのスナップショットもダウンロードできます。https://github.com/BVLC/caffe/tree/master/models/bvlc_googlenetを参照してください 繰り返しますが、すべての事前に重み付けされた.caffemodelファイルが必要です。 .caffemodelファイルはhttp://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodelからダウンロードできます

これらの.caffemodelファイルを手元に置いて、DIGITにアップロードすることができます。 DIGITホームページのPretrained Modelsタブに移動し、 Pretrained Modelアップロードを選択します:

これらの事前トレーニングされたモデルの両方で、DIGITが提供するデフォルト(256 x 256の色、潰れた画像)を使用できます。 Weights (**.caffemodel) Model Definition (original.prototxt) Weights (**.caffemodel)Model Definition (original.prototxt)を提供するだけです。 各ボタンをクリックしてファイルを選択します。

モデル定義については、GoogLeNetとhttps://github.com/BVLC/caffe/blob/master/modelsの https://github.com/BVLC/caffe/blob/master/models/bvlc_googlenet/train_val.prototxtを使用できます。 AlexNetの/bvlc_alexnet/train_val.prototxt これらのネットワークの分類ラベルは使用しないので、 labels.txtファイルを追加することはスキップします。

AlexNetとGoogLeNetの両方でこのプロセスを繰り返してください。次の手順で両方を使用します。

Q:「微調整の基礎となるネットワークは他にありますか?」

Caffe Model Zooには、使用可能なその他のネットワークがかなりあります。https://github.com/BVLC/caffe/wiki/Model-Zooを参照してください

イルカとシーホーのための微調整AlexNet

事前にトレーニングされたCaffeモデルを使用してネットワークをトレーニングすることは、最初から始めることに似ていますが、いくつか調整する必要があります。 まず、 Base Learning Rateを0.001から0.01に調整します。そのような大きなジャンプは必要ありません(つまり、微調整しています)。 また、 Pretrained Networkを使用してカスタマイズします。

事前モデル化されたモデルの定義(すなわち、原文)では、最終的な完全結合層 (最終的な結果分類が行われる)へのすべての参照の名前を変更する必要があります。 これは、モデルが元のトレーニングデータに対してデータセットから新しいカテゴリを再学習するようにするためです(つまり、現在の最終レイヤを破棄したい)。 最後に完全に接続されたレイヤーの名前を “fc8″から “fc9″などに変更する必要があります。 最後に、 num_output2変更して、カテゴリの数を1000から2に調整する必要もあります。

ここでは、変更が必要です:

@@ -332,8 +332,8 @@
 }
 layer {
-  name: "fc8"
+  name: "fc9"
   type: "InnerProduct"
   bottom: "fc7"
-  top: "fc8"
+  top: "fc9"
   param {
     lr_mult: 1
@@ -345,5 +345,5 @@
   }
   inner_product_param {
-    num_output: 1000
+    num_output: 2
     weight_filler {
       type: "gaussian"
@@ -359,5 +359,5 @@
   name: "accuracy"
   type: "Accuracy"
-  bottom: "fc8"
+  bottom: "fc9"
   bottom: "label"
   top: "accuracy"
@@ -367,5 +367,5 @@
   name: "loss"
   type: "SoftmaxWithLoss"
-  bottom: "fc8"
+  bottom: "fc9"
   bottom: "label"
   top: "loss"
@@ -375,5 +375,5 @@
   name: "softmax"
   type: "Softmax"
-  bottom: "fc8"
+  bottom: "fc9"
   top: "softmax"
   include { stage: "deploy" }

src / alexnet-customized.prototxtで使用している完全に変更されたファイルを含めました。

今回は、精度が〜60%で始まり、すぐに87.5%に上昇し、次に96%に達して100%まで上昇し、損失は着実に減少しています。 5分後、100%の精度と0.0009の損失で終了します。

私たちの以前のネットワークが間違っていたのと同じシホルズの画像をテストすると、完全な逆転:100%のシホルスが見られます。

海藻の子供の絵画でさえもうまくいきます:

同じことはイルカにとっても同じです:

あなたが考えることが難しいと思われる画像であっても、複数のイルカが近くにあり、体が主に水中であるこのようなものであっても、正しいことを行います:

トレーニング:試行3、微調整GoogLeNet

以前のAlexNetモデルと同様に、微調整にも使われました.GoogLeNetも使用できます。 完全に接続された3つのレイヤーを1つではなく再定義する必要があるため、ネットワークの変更は少し面倒です。

GoogleのユースケースのためにGoogLeNetを微調整するには、もう一度新しい分類モデルを作成する必要があります。

完全に接続された3つの分類レイヤー、 loss1/classifierloss2/classifier 、およびloss3/classifierへの参照をすべて変更し、カテゴリ数( num_output: 2 )を再定義します。 3つのクラシファイアレイヤの名前を変更したり、1000のカテゴリから2つのカテゴリに変更したりするために必要な変更は次のとおりです。

@@ -917,10 +917,10 @@
   exclude { stage: "deploy" }
 }
 layer {
-  name: "loss1/classifier"
+  name: "loss1a/classifier"
   type: "InnerProduct"
   bottom: "loss1/fc"
-  top: "loss1/classifier"
+  top: "loss1a/classifier"
   param {
     lr_mult: 1
     decay_mult: 1
@@ -930,7 +930,7 @@
     decay_mult: 0
   }
   inner_product_param {
-    num_output: 1000
+    num_output: 2
     weight_filler {
       type: "xavier"
       std: 0.0009765625
@@ -945,7 +945,7 @@
 layer {
   name: "loss1/loss"
   type: "SoftmaxWithLoss"
-  bottom: "loss1/classifier"
+  bottom: "loss1a/classifier"
   bottom: "label"
   top: "loss1/loss"
   loss_weight: 0.3
@@ -954,7 +954,7 @@
 layer {
   name: "loss1/top-1"
   type: "Accuracy"
-  bottom: "loss1/classifier"
+  bottom: "loss1a/classifier"
   bottom: "label"
   top: "loss1/accuracy"
   include { stage: "val" }
@@ -962,7 +962,7 @@
 layer {
   name: "loss1/top-5"
   type: "Accuracy"
-  bottom: "loss1/classifier"
+  bottom: "loss1a/classifier"
   bottom: "label"
   top: "loss1/accuracy-top5"
   include { stage: "val" }
@@ -1705,10 +1705,10 @@
   exclude { stage: "deploy" }
 }
 layer {
-  name: "loss2/classifier"
+  name: "loss2a/classifier"
   type: "InnerProduct"
   bottom: "loss2/fc"
-  top: "loss2/classifier"
+  top: "loss2a/classifier"
   param {
     lr_mult: 1
     decay_mult: 1
@@ -1718,7 +1718,7 @@
     decay_mult: 0
   }
   inner_product_param {
-    num_output: 1000
+    num_output: 2
     weight_filler {
       type: "xavier"
       std: 0.0009765625
@@ -1733,7 +1733,7 @@
 layer {
   name: "loss2/loss"
   type: "SoftmaxWithLoss"
-  bottom: "loss2/classifier"
+  bottom: "loss2a/classifier"
   bottom: "label"
   top: "loss2/loss"
   loss_weight: 0.3
@@ -1742,7 +1742,7 @@
 layer {
   name: "loss2/top-1"
   type: "Accuracy"
-  bottom: "loss2/classifier"
+  bottom: "loss2a/classifier"
   bottom: "label"
   top: "loss2/accuracy"
   include { stage: "val" }
@@ -1750,7 +1750,7 @@
 layer {
   name: "loss2/top-5"
   type: "Accuracy"
-  bottom: "loss2/classifier"
+  bottom: "loss2a/classifier"
   bottom: "label"
   top: "loss2/accuracy-top5"
   include { stage: "val" }
@@ -2435,10 +2435,10 @@
   }
 }
 layer {
-  name: "loss3/classifier"
+  name: "loss3a/classifier"
   type: "InnerProduct"
   bottom: "pool5/7x7_s1"
-  top: "loss3/classifier"
+  top: "loss3a/classifier"
   param {
     lr_mult: 1
     decay_mult: 1
@@ -2448,7 +2448,7 @@
     decay_mult: 0
   }
   inner_product_param {
-    num_output: 1000
+    num_output: 2
     weight_filler {
       type: "xavier"
     }
@@ -2461,7 +2461,7 @@
 layer {
   name: "loss3/loss"
   type: "SoftmaxWithLoss"
-  bottom: "loss3/classifier"
+  bottom: "loss3a/classifier"
   bottom: "label"
   top: "loss"
   loss_weight: 1
@@ -2470,7 +2470,7 @@
 layer {
   name: "loss3/top-1"
   type: "Accuracy"
-  bottom: "loss3/classifier"
+  bottom: "loss3a/classifier"
   bottom: "label"
   top: "accuracy"
   include { stage: "val" }
@@ -2478,7 +2478,7 @@
 layer {
   name: "loss3/top-5"
   type: "Accuracy"
-  bottom: "loss3/classifier"
+  bottom: "loss3a/classifier"
   bottom: "label"
   top: "accuracy-top5"
   include { stage: "val" }
@@ -2489,7 +2489,7 @@
 layer {
   name: "softmax"
   type: "Softmax"
-  bottom: "loss3/classifier"
+  bottom: "loss3a/classifier"
   top: "softmax"
   include { stage: "deploy" }
 }

私は完全なファイルをsrc / googlenet-customized.prototxtに入れました

Q:「これらのネットワークのプロトタイプ定義の変更はどうですか?完全に接続されたレイヤー名とカテゴリ数を変更しましたか?それ以外に何が変わる可能性がありますか、変更する必要がありますか?

大きな質問ですが、それも私が疑問に思っているものです。 たとえば、重みを変更しないように特定のレイヤーを「修正」できることがわかっています。 他のことをするには、レイヤの仕組みを理解しておく必要があります。このガイドはこのガイドを超えており、現在もその作者を超えています。

AlexNetを微調整するのと同じように、学習率を0.01 %から0.001 %に10%引き下げます。

Q:「これらのネットワークを微調整するときには、他のどのような変更が必要でしょうか?エポック数、バッチサイズ、ソルバータイプ(Adam、AdaDelta、AdaGradなど)、学習率、ポリシー(指数関数的減衰、逆減衰、Sigmoid Decay等)、ステップサイズ、およびガンマ値?

偉大な疑問と、それについても私は不思議なものです。 私はこれらをあいまいに理解しているだけであり、トレーニングの際にこれらの値を変更する方法を知っていれば改善できる可能性があります。 これは、より良い文書を必要とするものです。

GoogLeNetはAlexNetよりも複雑なアーキテクチャなので、微調整にはより多くの時間が必要です。 私のラップトップでは、100%の精度と0.0070の損失を達成するために、データセットでGoogLeNetを再学習するのに10分かかります。

AlexNetの微調整バージョンで見たように、変更されたGoogLeNetはこれまでのところ最高の性能を発揮します。

私たちのモデルを使う

私たちのネットワークは訓練され、テストされているので、ダウンロードして使用する時が来ました。 DIGITSで訓練した各モデルには、 Download Modelボタンがあり、トレーニング実行中に異なるスナップショットを選択する方法もあります(例: Epoch #30 )。

Download Modelをクリックすると、以下のファイルを含むtar.gzアーカイブがダウンロードされます。

deploy.prototxt
mean.binaryproto
solver.prototxt
info.json
original.prototxt
labels.txt
snapshot_iter_90.caffemodel
train_val.prototxt

作成したばかりのモデルを使用する方法については、Caffeのドキュメントに素晴らしい説明があります。 それは言う:

ネットワークはそのデザイン(.prototxt)とその重み(.caffemodel)によって定義されます。 ネットワークが訓練されているとき、そのネットワークの重みの現在の状態は.caffemodelに保存されます。 これらの両方で、我々は列車/試験段階から生産段階に移行することができます。

現在の状態では、ネットワークの設計は展開用に設計されていません。 ネットワークを製品としてリリースするには、まずいくつかの方法でネットワークを変更する必要があります。

  1. 分類にはデータのラベルを提供していないため、トレーニングに使用されたデータレイヤーを削除します。
  2. データラベルに依存するレイヤーをすべて削除します。
  3. データを受け入れるようにネットワークを設定します。
  4. ネットワークに結果を出力させます。

DIGITSは既に私たちの仕事をしていて、 prototxtファイルの異なるバージョンをprototxtます。 このネットワークを使用する際に気になるファイルは次のとおりです。

  • deploy.prototxt – 私たちのネットワークの定義で、画像入力データを受け取る準備ができています。
  • mean.binaryproto – 私たちのモデルでは、処理している各画像から画像平均を差し引く必要があり、これが平均画像です。
  • labels.txt – 印刷する場合のラベル( dolphinseahorse )とカテゴリ番号のリスト
  • snapshot_iter_90.caffemodel – これは私たちのネットワークの訓練された重みです

We can use these files in a number of ways to classify new images. For example, in our CAFFE_ROOT we can use build/examples/cpp_classification/classification.bin to classify one image:

$ cd $CAFFE_ROOT/build/examples/cpp_classification
$ ./classification.bin deploy.prototxt snapshot_iter_90.caffemodel mean.binaryproto labels.txt dolphin1.jpg

This will spit out a bunch of debug text, followed by the predictions for each of our two categories:

0.9997 - “dolphin”
0.0003 - “seahorse”

You can read the complete C++ source for this in the Caffe examples .

For a classification version that uses the Python interface, DIGITS includes a nice example . There’s also a fairly well documented Python walkthrough in the Caffe examples.

Python example

Let’s write a program that uses our fine-tuned GoogLeNet model to classify the untrained images we have in data/untrained-samples . I’ve cobbled this together based on the examples above, as well as the caffe Python module’s source , which you should prefer to anything I’m about to say.

A full version of what I’m going to discuss is available in src/classify-samples.py . さぁ、始めよう!

First, we’ll need the NumPy module. In a moment we’ll be using NumPy to work with ndarray s , which Caffe uses a lot. If you haven’t used them before, as I had not, you’d do well to begin by reading this Quickstart tutorial .

Second, we’ll need to load the caffe module from our CAFFE_ROOT dir. If it’s not already included in your Python environment, you can force it to load by adding it manually. Along with it we’ll also import caffe’s protobuf module:

import numpy as np

caffe_root = '/path/to/your/caffe_root'
sys.path.insert(0, os.path.join(caffe_root, 'python'))
import caffe
from caffe.proto import caffe_pb2

Next we need to tell Caffe whether to use the CPU or GPU . For our experiments, the CPU is fine:

caffe.set_mode_cpu()

Now we can use caffe to load our trained network. To do so, we’ll need some of the files we downloaded from DIGITS, namely:

  • deploy.prototxt – our “network file”, the description of the network.
  • snapshot_iter_90.caffemodel – our trained “weights”

We obviously need to provide the full path, and I’ll assume that my files are in a dir called model/ :

model_dir = 'model'
deploy_file = os.path.join(model_dir, 'deploy.prototxt')
weights_file = os.path.join(model_dir, 'snapshot_iter_90.caffemodel')
net = caffe.Net(deploy_file, caffe.TEST, weights=weights_file)

The caffe.Net() constructor takes a network file, a phase ( caffe.TEST or caffe.TRAIN ), as well as an optional weights filename. When we provide a weights file, the Net will automatically load them for us. The Net has a number of methods and attributes you can use.

Note: There is also a deprecated version of this constructor , which seems to get used often in sample code on the web. It looks like this, in case you encounter it:

net = caffe.Net(str(deploy_file), str(model_file), caffe.TEST)

We’re interested in loading images of various sizes into our network for testing. As a result, we’ll need to transform them into a shape that our network can use (ie, colour, 256×256). Caffe provides the Transformer class for this purpose. We’ll use it to create a transformation appropriate for our images/network:

transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
# set_transpose: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L187
transformer.set_transpose('data', (2, 0, 1))
# set_raw_scale: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L221
transformer.set_raw_scale('data', 255)
# set_channel_swap: https://github.com/BVLC/caffe/blob/61944afd4e948a4e2b4ef553919a886a8a8b8246/python/caffe/io.py#L203
transformer.set_channel_swap('data', (2, 1, 0))

We can also use the mean.binaryproto file DIGITS gave us to set our transformer’s mean:

# This code for setting the mean from https://github.com/NVIDIA/DIGITS/tree/master/examples/classification
mean_file = os.path.join(model_dir, 'mean.binaryproto')
with open(mean_file, 'rb') as infile:
    blob = caffe_pb2.BlobProto()
    blob.MergeFromString(infile.read())
    if blob.HasField('shape'):
        blob_dims = blob.shape
        assert len(blob_dims) == 4, 'Shape should have 4 dimensions - shape is %s' % blob.shape
    elif blob.HasField('num') and blob.HasField('channels') and \
            blob.HasField('height') and blob.HasField('width'):
        blob_dims = (blob.num, blob.channels, blob.height, blob.width)
    else:
        raise ValueError('blob does not provide shape or 4d dimensions')
    pixel = np.reshape(blob.data, blob_dims[1:]).mean(1).mean(1)
    transformer.set_mean('data', pixel)

If we had a lot of labels, we might also choose to read in our labels file, which we can use later by looking up the label for a probability using its position (eg, 0=dolphin, 1=seahorse):

labels_file = os.path.join(model_dir, 'labels.txt')
labels = np.loadtxt(labels_file, str, delimiter='\n')

Now we’re ready to classify an image. We’ll use caffe.io.load_image() to read our image file, then use our transformer to reshape it and set it as our network’s data layer:

# Load the image from disk using caffe's built-in I/O module
image = caffe.io.load_image(fullpath)
# Preprocess the image into the proper format for feeding into the model
net.blobs['data'].data[...] = transformer.preprocess('data', image)

Q: “How could I use images (ie, frames) from a camera or video stream instead of files?”

Great question, here’s a skeleton to get you started:

import cv2
...
# Get the shape of our input data layer, so we can resize the image
input_shape = net.blobs['data'].data.shape
...
webCamCap = cv2.VideoCapture(0) # could also be a URL, filename
if webCamCap.isOpened():
    rval, frame = webCamCap.read()
else:
    rval = False

while rval:
    rval, frame = webCamCap.read()
    net.blobs['data'].data[...] = transformer.preprocess('data', frame)
    ...

webCamCap.release()

Back to our problem, we next need to run the image data through our network and read out the probabilities from our network’s final 'softmax' layer, which will be in order by label category:

# Run the image's pixel data through the network
out = net.forward()
# Extract the probabilities of our two categories from the final layer
softmax_layer = out['softmax']
# Here we're converting to Python types from ndarray floats
dolphin_prob = softmax_layer.item(0)
seahorse_prob = softmax_layer.item(1)

# Print the results. I'm using labels just to show how it's done
label = labels[0] if dolphin_prob > seahorse_prob else labels[1]
filename = os.path.basename(fullpath)
print '%s is a %s dolphin=%.3f%% seahorse=%.3f%%' % (filename, label, dolphin_prob*100, seahorse_prob*100)

Running the full version of this (see src/classify-samples.py ) using our fine-tuned GoogLeNet network on our data/untrained-samples images gives me the following output:

[...truncated caffe network output...]
dolphin1.jpg is a dolphin dolphin=99.968% seahorse=0.032%
dolphin2.jpg is a dolphin dolphin=99.997% seahorse=0.003%
dolphin3.jpg is a dolphin dolphin=99.943% seahorse=0.057%
seahorse1.jpg is a seahorse dolphin=0.365% seahorse=99.635%
seahorse2.jpg is a seahorse dolphin=0.000% seahorse=100.000%
seahorse3.jpg is a seahorse dolphin=0.014% seahorse=99.986%

I’m still trying to learn all the best practices for working with models in code. I wish I had more and better documented code examples, APIs, premade modules, etc to show you here. To be honest, most of the code examples I’ve found are terse, and poorly documented–Caffe’s documentation is spotty, and assumes a lot.

It seems to me like there’s an opportunity for someone to build higher-level tools on top of the Caffe interfaces for beginners and basic workflows like we’ve done here. It would be great if there were more simple modules in high-level languages that I could point you at that “did the right thing” with our model; someone could/should take this on, and make using Caffe models as easy as DIGITS makes training them. I’d love to have something I could use in node.js, for example. Ideally one shouldn’t be required to know so much about the internals of the model or Caffe. I haven’t used it yet, but DeepDetect looks interesting on this front, and there are likely many other tools I don’t know about.

結果

At the beginning we said that our goal was to write a program that used a neural network to correctly classify all of the images in data/untrained-samples . These are images of dolphins and seahorses that were never used in the training or validation data:

Untrained Dolphin Images

Untrained Seahorse Images

Let’s look at how each of our three attempts did with this challenge:

Model Attempt 1: AlexNet from Scratch (3rd Place)

画像 イルカ シーホース 結果
dolphin1.jpg 71.11% 28.89% 😑
dolphin2.jpg 99.2% 0.8% 😎
dolphin3.jpg 63.3% 36.7% 😕
seahorse1.jpg 95.04% 4.96% 😞
seahorse2.jpg 56.64% 43.36 😕
seahorse3.jpg 7.06% 92.94% 😁

Model Attempt 2: Fine Tuned AlexNet (2nd Place)

画像 イルカ シーホース 結果
dolphin1.jpg 99.1% 0.09% 😎
dolphin2.jpg 99.5% 0.05% 😎
dolphin3.jpg 91.48% 8.52% 😁
seahorse1.jpg 0% 100% 😎
seahorse2.jpg 0% 100% 😎
seahorse3.jpg 0% 100% 😎

Model Attempt 3: Fine Tuned GoogLeNet (1st Place)

画像 イルカ シーホース 結果
dolphin1.jpg 99.86% 0.14% 😎
dolphin2.jpg 100% 0% 😎
dolphin3.jpg 100% 0% 😎
seahorse1.jpg 0.5% 99.5% 😎
seahorse2.jpg 0% 100% 😎
seahorse3.jpg 0.02% 99.98% 😎

結論

It’s amazing how well our model works, and what’s possible by fine tuning a pretrained network. Obviously our dolphin vs. seahorse example is contrived, and the dataset overly limited–we really do want more and better data if we want our network to be robust. But since our goal was to examine the tools and workflows of neural networks, it’s turned out to be an ideal case, especially since it didn’t require expensive equipment or massive amounts of time.

Above all I hope that this experience helps to remove the overwhelming fear of getting started. Deciding whether or not it’s worth investing time in learning the theories of machine learning and neural networks is easier when you’ve been able to see it work in a small way. Now that you’ve got a setup and a working approach, you can try doing other sorts of classifications. You might also look at the other types of things you can do with Caffe and DIGITS, for example, finding objects within an image, or doing segmentation.

Have fun with machine learning!







-humphd
-, , , ,

執筆者: