基于预训练模型的图像分类

在本课中 , 您将学习使用预训练模型来检测给定图像中的对象。您将使用 squezenet 预训练模块 , 以很高的精度检测和分类给定图像中的对象。

打开一个新的 Juypter 笔记本 并按照步骤开发此图像分类应用程序。

正在导入库

首先 , 我们使用下面的代码导入所需的包 -

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

用于训练的图像显然会有不同的大小。所有这些图像都必须转换为固定大小才能进行准确的训练。同样 , 测试图像和您要在生产环境中预测的图像也必须转换为大小 , 与训练时使用的大小相同。因此 , 我们在上面创建了一个名为 INPUT _ IMAGE _ SIZE 有价值 227 。因此 , 我们将所有图像转换为大小 227x227 在我们的分类器中使用它之前。

我们还声明了一个名为的变量 平均值 有价值 128 , 稍后用于改进分类结果。

接下来 , 我们将开发两个函数来处理图像。

图像处理

图像处理包括两个步骤。第一个是调整图像大小 , 第二个是集中裁剪图像。对于这两个步骤 , 我们将编写两个调整大小和裁剪的函数。

图像大小调整

首先 , 我们将编写一个用于调整图像大小的函数 , 如前所述 , 我们将图像大小调整为 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

我们使用以下两行代码为图像创建一个新的起点 :

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')

输出如下 -

Processing Image

请注意 , 原始图像的大小为 600 x 960 。我们需要将其调整为我们的规格 227 x 227 。调用我们之前定义的 调整大小 函数完成这项工作。

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')

输出如下所示 -

Original 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')

下面提到的是代码的输出 -

Cropping Image

在这一点上 , 图像的大小 227 x 227 现在 , 我们交换图像轴 , 将三种颜色提取到三个不同的区域。

img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)

下面给出的是输出 -

CHW Image Shape: (3, 227, 227)

请注意 , 最后一个轴现在已成为数组中的第一个维度。我们现在将使用以下代码绘制三个通道 :

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))

输出如下 -

Dimension

最后 , 我们对图像进行一些额外的处理 , 如转换 红色绿色蓝色 to 蓝色绿色红色 ( 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 格式 接下来 , 我们将加载我们预先训练的模型文件 , 并将上面的图像输入到其中进行预测。

预测已处理图像中的对象

我们首先设置的路径 init and 预测 在 Caffe 的预训练模型中定义的网络。

设置模型文件路径

请记住 , 从我们前面的讨论中 , 所有预训练的模型都安装在 型号 文件夹。我们按如下方式设置此文件夹的路径 :

CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")

我们设置的路径 init _ net protobuf 文件的 squezenet 模型如下 -

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')

我们打印两条路径用于诊断目的 -

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

接下来 , 我们将创建一个预测器。

创建预测器

我们使用以下两个语句读取模型文件 :

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)

The p 对象是预测器 , 用于预测任何给定图像中的对象。请注意 , 每个输入图像必须采用 NCHW 格式 , 就像我们之前对 tree. jpg 文件。

预测对象

预测给定图像中的对象是微不足道的 - 只需执行一行命令。我们调用 运行 上的方法 预测器 用于给定图像中的对象检测的对象。

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)

现在可以通过获取 max 中的值 preds 数组。

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 with 89% confidence 。 984 的索引对我们理解检测到什么样的对象没有多大意义。我们需要使用其索引值获取对象的字符串化名称。模型识别的对象类型及其相应的索引值在 github 存储库中可用。

现在 , 我们将看到如何检索索引值为 984 的对象的名称。

Stringifying Result

我们创建一个指向 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 循环如下 :

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 文件被存储。在代码中更改图像文件的名称。只需要进行一次更改 , 如下所示

img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)

原始图片和预测结果如下所示 -

Predicting Image

输出在下面提到 -

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 。