Caffe2-image-classification-using-pre-trained-model
事前学習済みモデルを使用した画像分類
このレッスンでは、事前に訓練されたモデルを使用して、特定の画像内のオブジェクトを検出する方法を学習します。 squeezenet 事前学習済みモジュールを使用して、特定の画像内のオブジェクトを非常に正確に検出および分類します。
新しい Juypter Notebook を開き、手順に従ってこの画像分類アプリケーションを開発します。
ライブラリのインポート
まず、以下のコードを使用して必要なパッケージをインポートします-
from caffe2.proto import caffe2_pb2
from caffe2.python import core, workspace, models
import numpy as np
import skimage.io
import skimage.transform
from matplotlib import pyplot
import os
import urllib.request as urllib2
import operator
次に、いくつかの*変数*を設定します-
INPUT_IMAGE_SIZE = 227
mean = 128
トレーニングに使用される画像は明らかにさまざまなサイズになります。 これらすべての画像は、正確なトレーニングのために固定サイズに変換する必要があります。 同様に、テスト環境と実稼働環境で予測するイメージも、トレーニング中に使用したものと同じサイズに変換する必要があります。 したがって、値 227 を持つ INPUT_IMAGE_SIZE という上記の変数を作成します。 したがって、すべての画像をサイズ 227x227 に変換してから、分類器で使用します。
また、値 128 を持つ mean という変数を宣言します。この変数は、後で分類結果を改善するために使用されます。
次に、画像を処理するための2つの関数を開発します。
画像処理
画像処理は2つのステップで構成されます。 1つ目は画像のサイズを変更することであり、2つ目は画像を中央でトリミングすることです。 これらの2つのステップでは、サイズ変更とトリミングのための2つの関数を作成します。
画像のサイズ変更
最初に、画像のサイズを変更する関数を作成します。 前述のように、画像のサイズを 227x227 に変更します。 だから私たちは次のように関数*リサイズ*を定義しましょう-
def resize(img, input_height, input_width):
幅を高さで割ることにより、画像のアスペクト比を取得します。
original_aspect = img.shape[1]/float(img.shape[0])
アスペクト比が1より大きい場合、画像が広いこと、つまりランドスケープモードであることを示します。 私たちは今、次のコードを使用して画像の高さを調整し、サイズ変更された画像を返します-
if(original_aspect>1):
new_height = int(original_aspect * input_height)
return skimage.transform.resize(img, (input_width,
new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
アスペクト比が 1 未満の場合、*縦モード*を示します。 次のコードを使用して幅を調整します-
if(original_aspect<1):
new_width = int(input_width/original_aspect)
return skimage.transform.resize(img, (new_width,
input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
アスペクト比が 1 の場合、高さ/幅の調整は行われません。
if(original_aspect == 1):
return skimage.transform.resize(img, (input_width,
input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
完全な機能コードは、クイックリファレンスのために以下に示されています-
def resize(img, input_height, input_width):
original_aspect = img.shape[1]/float(img.shape[0])
if(original_aspect>1):
new_height = int(original_aspect * input_height)
return skimage.transform.resize(img, (input_width,
new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
if(original_aspect<1):
new_width = int(input_width/original_aspect)
return skimage.transform.resize(img, (new_width,
input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
if(original_aspect == 1):
return skimage.transform.resize(img, (input_width,
input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
次に、画像の中心を切り取るための関数を作成します。
画像のトリミング
次のように crop_image 関数を宣言します-
def crop_image(img,cropx,cropy):
私たちは次のステートメントを使用して画像の寸法を抽出します-
y,x,c = img.shape
次の2行のコードを使用して、画像の新しい開始点を作成します-
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
最後に、新しい次元で画像オブジェクトを作成することにより、トリミングされた画像を返します-
return img[starty:starty+cropy,startx:startx+cropx]
クイックリファレンスのために、機能コード全体を以下に示します-
def crop_image(img,cropx,cropy):
y,x,c = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx]
次に、これらの機能をテストするコードを作成します。
画像処理
まず、画像ファイルをプロジェクトディレクトリ内の images サブフォルダーにコピーします。 tree.jpg ファイルがプロジェクトにコピーされます。 次のPythonコードは画像を読み込み、コンソールに表示します-
img = skimage.img_as_float(skimage.io.imread("images/tree.jpg")).astype(np.float32)
print("Original Image Shape: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Original image')
出力は次のとおりです-
元の画像のサイズは 600 x 960 であることに注意してください。 このサイズを 227 x 227 の仕様に合わせて変更する必要があります。 以前に定義した* resize *関数を呼び出すと、このジョブが実行されます。
img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after resizing: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Resized image')
出力は以下のとおりです-
画像サイズは 227 x 363 になりました。 アルゴリズムへの最終フィードのために、これを 227 x 227 にトリミングする必要があります。 この目的のために、以前に定義されたクロップ関数を呼び出します。
img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after cropping: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Center Cropped')
以下は、コードの出力です-
この時点で、画像のサイズは 227 x 227 であり、さらに処理する準備ができています。 画像軸を交換して、3つの色を3つの異なるゾーンに抽出します。
img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)
以下が出力です-
CHW Image Shape: (3, 227, 227)
最後の軸が配列の最初の次元になっていることに注意してください。 次のコードを使用して3つのチャネルをプロットします-
pyplot.figure()
for i in range(3):
pyplot.subplot(1, 3, i+1)
pyplot.imshow(img[i])
pyplot.axis('off')
pyplot.title('RGB channel %d' % (i+1))
出力は以下のとおりです-
最後に、次の3行のコードを使用して、 Red Green Blue を* Blue Green Red(RGBからBGR)*に変換し、より良い結果を得るために平均値を削除し、バッチサイズ軸を追加するなど、画像に対していくつかの追加処理を行います-
# convert RGB --> BGR
img = img[(2, 1, 0), :, :]
# remove mean
img = img * 255 - mean
# add batch size axis
img = img[np.newaxis, :, :, :].astype(np.float32)
この時点で、画像は* NCHW形式*であり、ネットワークに送信する準備ができています。 次に、事前に訓練されたモデルファイルを読み込み、予測のために上記の画像をそのファイルにフィードします。
処理されたイメージ内のオブジェクトの予測
最初に、Caffeの事前トレーニングモデルで定義された init および predict ネットワークのパスを設定します。
モデルファイルパスの設定
前の説明から、事前に訓練されたモデルはすべて models フォルダーにインストールされていることを思い出してください。 次のようにこのフォルダへのパスを設定します-
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
次のように、 squeezenet モデルの init_net protobufファイルへのパスを設定します-
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
同様に、次のように predict_net protobufへのパスを設定します-
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
診断目的で2つのパスを印刷します-
print(INIT_NET)
print(PREDICT_NET)
出力と一緒に上記のコードは、あなたのクイックリファレンスのためにここに与えられています-
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)
出力は以下に記載されています-
/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/init_net.pb
/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/predict_net.pb
次に、予測変数を作成します。
予測子の作成
私たちは次の2つのステートメントを使用してモデルファイルを読み取ります-
with open(INIT_NET, "rb") as f:
init_net = f.read()
with open(PREDICT_NET, "rb") as f:
predict_net = f.read()
予測子は、2つのファイルへのポインターをパラメーターとして Predictor 関数に渡すことによって作成されます。
p = workspace.Predictor(init_net, predict_net)
*p* オブジェクトは予測子であり、任意の画像内のオブジェクトを予測するために使用されます。 各入力画像は、 *tree.jpg* ファイルに対して以前に行ったように、NCHW形式でなければならないことに注意してください。
オブジェクトの予測
特定の画像内のオブジェクトを予測するのは簡単です。コマンドを1行実行するだけです。 特定の画像でオブジェクトを検出するために、 predictor オブジェクトで run メソッドを呼び出します。
results = p.run({'data': img})
予測結果は results オブジェクトで利用できるようになりました。これを読みやすくするために配列に変換します。
results = np.asarray(results)
次のステートメントを使用して理解のために配列の次元を印刷します-
print("results shape: ", results.shape)
出力は次のようになります-
results shape: (1, 1, 1000, 1, 1)
ここで不要な軸を削除します-
preds = np.squeeze(results)
*preds* 配列の *max* 値を取得することで、最上位の述語を取得できるようになりました。
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
出力は次のとおりです-
Prediction: 984
Confidence: 0.89235985
ご覧のように、モデルはインデックス値 984 のオブジェクトを* 89%*の信頼度で予測しています。 984のインデックスは、検出されたオブジェクトの種類を理解する上であまり意味がありません。 インデックス値を使用して、オブジェクトの文字列化された名前を取得する必要があります。 モデルが認識するオブジェクトの種類とそれに対応するインデックス値は、githubリポジトリで利用できます。
ここで、984のインデックス値を持つオブジェクトの名前を取得する方法を確認します。
結果の文字列化
次のようにgithubリポジトリへのURLオブジェクトを作成します-
codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac0
71eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"
私たちはURLの内容を読みます-
response = urllib2.urlopen(codes)
応答には、すべてのコードとその説明のリストが含まれます。 応答のいくつかの行は、それが何を含むかを理解するために以下に示されています-
5: 'electric ray, crampfish, numbfish, torpedo',
6: 'stingray',
7: 'cock',
8: 'hen',
9: 'ostrich, Struthio camelus',
10: 'brambling, Fringilla montifringilla',
次のように for ループを使用して、配列全体を反復処理して984の目的のコードを見つけます-
for line in response:
mystring = line.decode('ascii')
code, result = mystring.partition(":")[::2]
code = code.strip()
result = result.replace("'", "")
if (code == str(curr_pred)):
name = result.split(",")[0][1:]
print("Model predicts", name, "with", curr_conf, "confidence")
あなたがコードを実行すると、次の出力が表示されます-
Model predicts rapeseed with 0.89235985 confidence
これで、別の画像でモデルを試すことができます。
別の画像を予測する
別の画像を予測するには、画像ファイルをプロジェクトディレクトリの images フォルダーにコピーするだけです。 これは、以前の tree.jpg ファイルが保存されているディレクトリです。 コード内の画像ファイルの名前を変更します。 以下に示すように、必要な変更は1つだけです
img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)
元の画像と予測結果を以下に示します-
出力は以下に記載されています-
Model predicts pretzel with 0.99999976 confidence
ご覧のとおり、事前学習済みのモデルは、指定された画像内のオブジェクトを非常に正確に検出できます。
完全なソース
所定の画像内のオブジェクト検出に事前に訓練されたモデルを使用する上記のコードの完全なソースは、クイックリファレンスのためにここに記載されています-
def crop_image(img,cropx,cropy):
y,x,c = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx]
img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)
print("Original Image Shape: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Original image')
img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after resizing: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Resized image')
img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after cropping: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Center Cropped')
img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)
pyplot.figure()
for i in range(3):
pyplot.subplot(1, 3, i+1)
pyplot.imshow(img[i])
pyplot.axis('off')
pyplot.title('RGB channel %d' % (i+1))
# convert RGB --> BGR
img = img[(2, 1, 0), :, :]
# remove mean
img = img * 255 - mean
# add batch size axis
img = img[np.newaxis, :, :, :].astype(np.float32)
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)
with open(INIT_NET, "rb") as f:
init_net = f.read()
with open(PREDICT_NET, "rb") as f:
predict_net = f.read()
p = workspace.Predictor(init_net, predict_net)
results = p.run({'data': img})
results = np.asarray(results)
print("results shape: ", results.shape)
preds = np.squeeze(results)
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac071eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"
response = urllib2.urlopen(codes)
for line in response:
mystring = line.decode('ascii')
code, result = mystring.partition(":")[::2]
code = code.strip()
result = result.replace("'", "")
if (code == str(curr_pred)):
name = result.split(",")[0][1:]
print("Model predicts", name, "with", curr_conf, "confidence")
この時点で、事前学習済みモデルを使用してデータセットの予測を行う方法がわかっています。
次は、 Caffe2 で*ニューラルネットワーク(NN)*アーキテクチャを定義し、データセットでトレーニングする方法を学習します。 ここで、単純な単一層NNを作成する方法を学習します。