Phân loại hình ảnh sử dụng mô hình đào tạo trước
Trong bài học này, bạn sẽ học cách sử dụng một mô hình được đào tạo trước để phát hiện các đối tượng trong một hình ảnh nhất định. Bạn sẽ sử dụngsqueezenet mô-đun được đào tạo trước giúp phát hiện và phân loại các đối tượng trong một hình ảnh nhất định với độ chính xác cao.
Mở một cái mới Juypter notebook và làm theo các bước để phát triển ứng dụng phân loại ảnh này.
Nhập thư viện
Đầu tiên, chúng tôi nhập các gói được yêu cầu bằng mã bên dưới -
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
Tiếp theo, chúng tôi thiết lập một số variables -
INPUT_IMAGE_SIZE = 227
mean = 128
Các hình ảnh được sử dụng để đào tạo rõ ràng sẽ có kích thước khác nhau. Tất cả những hình ảnh này phải được chuyển đổi thành một kích thước cố định để đào tạo chính xác. Tương tự như vậy, hình ảnh thử nghiệm và hình ảnh bạn muốn dự đoán trong môi trường sản xuất cũng phải được chuyển đổi thành kích thước, giống như hình ảnh được sử dụng trong quá trình đào tạo. Do đó, chúng tôi tạo một biến ở trên được gọi làINPUT_IMAGE_SIZE có giá trị 227. Do đó, chúng tôi sẽ chuyển đổi tất cả hình ảnh của mình sang kích thước227x227 trước khi sử dụng nó trong bộ phân loại của chúng tôi.
Chúng tôi cũng khai báo một biến được gọi là mean có giá trị 128, được sử dụng sau này để cải thiện kết quả phân loại.
Tiếp theo, chúng tôi sẽ phát triển hai chức năng để xử lý hình ảnh.
Đang xử lý hình ảnh
Quá trình xử lý hình ảnh bao gồm hai bước. Đầu tiên là thay đổi kích thước hình ảnh, và thứ hai là cắt hình ảnh một cách trung tâm. Đối với hai bước này, chúng ta sẽ viết hai hàm thay đổi kích thước và cắt xén.
Thay đổi kích thước hình ảnh
Đầu tiên, chúng ta sẽ viết một hàm để thay đổi kích thước hình ảnh. Như đã nói trước đó, chúng tôi sẽ thay đổi kích thước hình ảnh thành227x227. Vì vậy, chúng ta hãy xác định hàmresize như sau -
def resize(img, input_height, input_width):
Chúng tôi thu được tỷ lệ khung hình của hình ảnh bằng cách chia chiều rộng cho chiều cao.
original_aspect = img.shape[1]/float(img.shape[0])
Nếu tỷ lệ khung hình lớn hơn 1, nó chỉ ra rằng hình ảnh rộng, có nghĩa là nó đang ở chế độ ngang. Bây giờ chúng tôi điều chỉnh chiều cao hình ảnh và trả lại hình ảnh đã thay đổi kích thước bằng cách sử dụng mã sau:
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)
Nếu tỷ lệ khung hình là less than 1, nó chỉ ra portrait mode. Bây giờ chúng tôi điều chỉnh chiều rộng bằng cách sử dụng mã sau:
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)
Nếu tỷ lệ khung hình bằng 1, chúng tôi không thực hiện bất kỳ điều chỉnh chiều cao / chiều rộng nào.
if(original_aspect == 1):
return skimage.transform.resize(img, (input_width,
input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
Mã chức năng đầy đủ được cung cấp bên dưới để bạn tham khảo nhanh -
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)
Bây giờ chúng ta sẽ viết một hàm để cắt hình ảnh xung quanh tâm của nó.
Cắt hình ảnh
Chúng tôi tuyên bố crop_image chức năng như sau -
def crop_image(img,cropx,cropy):
Chúng tôi trích xuất các kích thước của hình ảnh bằng cách sử dụng câu lệnh sau:
y,x,c = img.shape
Chúng tôi tạo điểm bắt đầu mới cho hình ảnh bằng hai dòng mã sau:
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
Cuối cùng, chúng tôi trả lại hình ảnh đã cắt bằng cách tạo một đối tượng hình ảnh với kích thước mới -
return img[starty:starty+cropy,startx:startx+cropx]
Toàn bộ mã chức năng được cung cấp bên dưới để bạn tham khảo nhanh -
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]
Bây giờ, chúng ta sẽ viết mã để kiểm tra các chức năng này.
Xử lý hình ảnh
Đầu tiên, sao chép một tệp hình ảnh vào images thư mục con trong thư mục dự án của bạn. tree.jpgtệp được sao chép trong dự án. Đoạn mã Python sau tải hình ảnh và hiển thị trên bảng điều khiển:
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')
Kết quả như sau:
Lưu ý rằng kích thước của hình ảnh gốc là 600 x 960. Chúng tôi cần thay đổi kích thước này thành đặc điểm kỹ thuật của chúng tôi227 x 227. Gọi điện được xác định trước đó của chúng tôiresizechức năng thực hiện công việc này.
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')
Đầu ra như dưới đây:
Lưu ý rằng bây giờ kích thước hình ảnh là 227 x 363. Chúng ta cần cắt nó để227 x 227cho nguồn cấp dữ liệu cuối cùng cho thuật toán của chúng tôi. Chúng tôi gọi hàm crop được xác định trước đó cho mục đích này.
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')
Dưới đây được đề cập là đầu ra của mã -
Tại thời điểm này, hình ảnh có kích thước 227 x 227và sẵn sàng để xử lý thêm. Bây giờ chúng ta hoán đổi các trục hình ảnh để trích xuất ba màu thành ba vùng khác nhau.
img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)
Dưới đây là kết quả -
CHW Image Shape: (3, 227, 227)
Lưu ý rằng trục cuối cùng bây giờ đã trở thành kích thước đầu tiên trong mảng. Bây giờ chúng ta sẽ vẽ sơ đồ ba kênh bằng đoạn mã sau:
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))
Đầu ra được nêu dưới đây -
Cuối cùng, chúng tôi thực hiện một số xử lý bổ sung trên hình ảnh, chẳng hạn như chuyển đổi Red Green Blue đến Blue Green Red (RGB to BGR), loại bỏ giá trị trung bình để có kết quả tốt hơn và thêm trục kích thước lô bằng cách sử dụng ba dòng mã sau:
# 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)
Tại thời điểm này, hình ảnh của bạn đang ở NCHW formatvà đã sẵn sàng để đưa vào mạng của chúng tôi. Tiếp theo, chúng tôi sẽ tải các tệp mô hình được đào tạo trước của mình và nạp hình ảnh trên vào đó để dự đoán.
Dự đoán các đối tượng trong hình ảnh đã xử lý
Đầu tiên, chúng tôi thiết lập các đường dẫn cho init và predict mạng được xác định trong các mô hình Caffe được đào tạo trước.
Đặt đường dẫn tệp mô hình
Hãy nhớ từ cuộc thảo luận trước đó của chúng tôi, tất cả các mô hình được đào tạo trước đều được cài đặt trong modelsthư mục. Chúng tôi thiết lập đường dẫn đến thư mục này như sau:
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
Chúng tôi thiết lập đường dẫn đến init_net tệp protobuf của squeezenet mô hình như sau -
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
Tương tự như vậy, chúng tôi thiết lập đường dẫn đến predict_net protobuf như sau -
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
Chúng tôi in hai đường dẫn cho mục đích chẩn đoán -
print(INIT_NET)
print(PREDICT_NET)
Đoạn mã trên cùng với đầu ra được đưa ra ở đây để bạn tham khảo nhanh -
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)
Đầu ra được đề cập bên dưới -
/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
Tiếp theo, chúng tôi sẽ tạo một công cụ dự đoán.
Tạo dự đoán
Chúng tôi đọc các tệp mô hình bằng hai câu lệnh sau:
with open(INIT_NET, "rb") as f:
init_net = f.read()
with open(PREDICT_NET, "rb") as f:
predict_net = f.read()
Dự đoán được tạo bằng cách chuyển con trỏ đến hai tệp dưới dạng tham số cho Predictor chức năng.
p = workspace.Predictor(init_net, predict_net)
Các pđối tượng là dự đoán, được sử dụng để dự đoán các đối tượng trong bất kỳ hình ảnh nhất định nào. Lưu ý rằng mỗi hình ảnh đầu vào phải ở định dạng NCHW như những gì chúng tôi đã làm trước đó vớitree.jpg tập tin.
Dự đoán đối tượng
Để dự đoán các đối tượng trong một hình ảnh nhất định là điều tầm thường - chỉ cần thực hiện một dòng lệnh duy nhất. Chúng tôi gọirun phương pháp trên predictor đối tượng để phát hiện đối tượng trong một hình ảnh nhất định.
results = p.run({'data': img})
Kết quả dự đoán hiện đã có trong results đối tượng mà chúng tôi chuyển đổi thành một mảng để chúng tôi dễ đọc.
results = np.asarray(results)
In các kích thước của mảng để bạn hiểu bằng cách sử dụng câu lệnh sau:
print("results shape: ", results.shape)
Đầu ra như hình dưới đây -
results shape: (1, 1, 1000, 1, 1)
Bây giờ chúng ta sẽ loại bỏ trục không cần thiết -
preds = np.squeeze(results)
Dự đoán trên cùng hiện có thể được truy xuất bằng cách lấy max giá trị trong preds mảng.
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
Kết quả như sau:
Prediction: 984
Confidence: 0.89235985
Như bạn thấy, mô hình đã dự đoán một đối tượng có giá trị chỉ mục 984 với 89%sự tự tin. Chỉ số 984 không có nhiều ý nghĩa đối với chúng tôi trong việc hiểu loại đối tượng nào được phát hiện. Chúng ta cần lấy tên được xâu chuỗi cho đối tượng bằng giá trị chỉ mục của nó. Loại đối tượng mà mô hình nhận dạng cùng với các giá trị chỉ mục tương ứng của chúng có sẵn trên kho lưu trữ github.
Bây giờ, chúng ta sẽ xem cách lấy tên cho đối tượng của chúng ta có giá trị chỉ mục là 984.
Chuỗi kết quả
Chúng tôi tạo một đối tượng URL cho kho lưu trữ github như sau:
codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac0
71eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"
Chúng tôi đọc nội dung của URL -
response = urllib2.urlopen(codes)
Phản hồi sẽ chứa danh sách tất cả các mã và mô tả của nó. Dưới đây là một vài dòng phản hồi để bạn hiểu về những gì nó chứa -
5: 'electric ray, crampfish, numbfish, torpedo',
6: 'stingray',
7: 'cock',
8: 'hen',
9: 'ostrich, Struthio camelus',
10: 'brambling, Fringilla montifringilla',
Bây giờ chúng tôi lặp lại toàn bộ mảng để xác định mã 984 mong muốn của chúng tôi bằng cách sử dụng for vòng lặp như sau -
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")
Khi bạn chạy mã, bạn sẽ thấy kết quả sau:
Model predicts rapeseed with 0.89235985 confidence
Bây giờ bạn có thể thử mô hình trên một hình ảnh khác.
Dự đoán một hình ảnh khác
Để dự đoán một hình ảnh khác, chỉ cần sao chép tệp hình ảnh vào imagesthư mục của thư mục dự án của bạn. Đây là thư mục mà trước đó của chúng tôitree.jpgtệp được lưu trữ. Thay đổi tên của tệp hình ảnh trong mã. Chỉ cần một thay đổi như hình bên dưới
img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)
Hình ảnh ban đầu và kết quả dự đoán được hiển thị bên dưới -
Đầu ra được đề cập bên dưới -
Model predicts pretzel with 0.99999976 confidence
Như bạn thấy, mô hình được đào tạo trước có thể phát hiện các đối tượng trong một hình ảnh nhất định với độ chính xác cao.
Nguồn đầy đủ
Nguồn đầy đủ cho đoạn mã trên sử dụng mô hình được đào tạo trước để phát hiện đối tượng trong một hình ảnh nhất định được đề cập ở đây để bạn tham khảo nhanh -
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")
Đến lúc này, bạn biết cách sử dụng mô hình được đào tạo trước để thực hiện các dự đoán trên tập dữ liệu của mình.
Điều tiếp theo là tìm hiểu cách xác định neural network (NN) kiến trúc trong Caffe2và đào tạo họ trên tập dữ liệu của bạn. Bây giờ chúng ta sẽ học cách tạo một NN đơn lớp tầm thường.