ساخت سیستم تشخیص ماسک با فناوری پهپاد و یادگیری عمیق – بخش دوم

به بخش دوم ساخت سیستم نظارتی تشخیص ماسک صورت با استفاده از تکنولوژی پهپاد و یادگیری عمیق خوش آمدید! در بخش اول این آموزش، درباره ­ی تکنولوژی پهپاد ، انواع طبقه­ بندی­ ها ، معماری پهپادی که در این پروژه از آن استفاده می ­شود و همچنین پیکربندی محیط برای برنامه ­نویسی پهپاد با پایتون ، صحبت شد. این بخش به چگونگی بارگذاری و پیش ­پردازش مجموعه ­داده ­های ماسک صورت می­ پردازد، ساخت مدل تشخیص دهنده ماسک صورت با استفاده از تنسورفلو / Keras را شرح می­ دهد و چگونگی اجرای آموزش یک مدل و ذخیره­ سازی آن مدل یادگیری عمیق برای پیاده­ سازی­ های آتی را توصیف می­ کند.

تشخیص ماسک

پیش نیاز ها

درک کامل این آموزش ، پیش نیاز هایی لازم دارد که عبارتند از:

  • داشتن شناخت اولیه از یادگیری عمیق با استفاده از تنسورفلو / Keras
  • نصب بودن پایتون ( نسخه­ ی ۳٫۵ به بعد ) و تنسورفلو ( نسخه ­ی ۲٫۰ به بعد ) بر روی سیستم خود
  • خواندن آموزش قبلی

در زیر تعدادی لینک معرفی شده است که به شما برای درک بهتر این آموزش کمک خواهند کرد:

حال نوبت این است که ساخت سیستم نظارتی را شروع کنیم. در این بخش، مدل یادگیری عمیق برای تشخیص دو کلاس افراد با ماسک و افراد بدون ماسک ساخته می ­شود.

برای انجام این طبقه ­بندی ، از یک دسته از شبکه ­های عصبی عمیق به نام شبکه ­های عصبی کانولوشنی استفاده می­ شود که معمولا برای تحلیل تصاویر به کار می ­رود.

بارگذاری و پیش­ پردازش مجموعه داده ­ها 

داده ، هسته­ ی اصلی هر الگوریتم هوش مصنوعی / یادگیری ماشین است. در این پروژه، از مجموعه داده ­های کگل و RMFD استفاده شده است. مجموعه داده شامل ۳۸۳۵ تصویر است که ۱۹۱۶ تصویر آن به کلاس افراد بدون ماسک و ۱۹۱۹ تصویر آن به کلاس افراد با ماسک تعلق دارند.

جهت ساخت این مدل، لازم است ابتدای کتابخانه­ های ضروری به پروژه وارد شوند. این کتابخانه ­ها دربرگیرنده ­ی ماژول­ هایی برای پیش ­پردازش مجموعه داده ، مدیریت فایل ، ساخت مدل و ارزیابی و بصری­ سازی آن مدل هستند.

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.layers import AveragePooling2D
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.utils import to_categorical
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import os

هوش مصنوعی Neptune ، یک تجمیع مرتب از هر دو سکوی نپتیون و TensorBoard ساخته است که به برنامه نویسی TensorBoard کمک می کند. اگر کتابخانه ­های ذیل به پروژه وارد می­ شوند می­توان از این سرویس بهره برد.

import random
import psutil
import neptune
import neptune_tensorboard as neptune_tb

سپس، اعتبار نامه ­های API نپتیون با استفاده از ماژول dotenv  از .env بارگذاری می­ شوند. توکنِ API، ارتباط بین اسکریپ­ های آموزش مدل و نپتیون را تصویب / رد می­ کند.  توکن API نپتیون مانند یک رمز عبور برای کاربرد نپتیون عمل می­ کند. بنابراین جهت محفوظ و مخفی ماندن این توکن از متغیر های محیطی برای بارگذاری مقدار آن بهره برده می ­شود. متغیرهای محیطی از کتابخانه dotenv  استفاده می­ کنند.

from dotenv import load_dotenv
load_dotenv()

API_SECRET = os.getenv("NEPTUNE_API_TOKEN")

سپس، پروژه راه ­اندازی می ­شود و به شکل خودکار رویداد های مرتبط با سنجه­ های TensorBoard را ثبت می­ کند.

<pre class="hljs" style="display: block; overflow-x: auto; padding: 0.5em; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">neptune.init(project_qualified_name=<span class="hljs-string" style="color: rgb(221, 17, 68);">'codebrain/Drone'</span>,
         	api_token=API_SECRET,
         	)
neptune_tb.integrate_with_tensorflow()
</pre>

بعد از انجام کارهای فوق، می­ توان تنظیمات محیط برنامه نویسی برای آموزش مدل تشخیص ماسک صورت را انجام داد. از طرفی نرخ اولیه یادگیری، تعداد Epoche ها برای آموزش مدل و اندازه­ ی دسته معین می ­شوند و مسیر رویداد های تجربه نیز تنظیم می ­شود.

PARAMS = {
  'EPOCHS': 20,
  'BS': 32,
  'INIT_LR': 1e-4,
}
RUN_NAME = 'run_{}'.format(random.getrandbits(64))
EXPERIMENT_LOG_DIR = 'logs/{}'.format(RUN_NAME)

در قدم بعد، تجزیه ­کننده ­های آرگومانی معرفی می­ شوند. تجزیه­ کننده­ های آرگومانی ، نوشتن رابط­ های خط فرمانِ کاربر پسند را تسهیل می ­بخشند. این رابط­ ها برای تعامل با مجموعه داده، طرح و مدل ایجاد می­ شوند.

ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True,
            	help="path to input dataset")
ap.add_argument("-p", "--plot", type=str, default="plot.png",
            	help="path to output loss/accuracy plot")
ap.add_argument("-m", "--model", type=str,
            	default="mask_detector.model",
            	help="path to output face mask detector model")
args = vars(ap.parse_args())

در قدم بعد، مسیر های هر یک از تصاویر از مسیر ذخیره ­سازی مجموعه داده دریافت می ­شوند و دو لیست به ترتیب برای نگهداری داده­ ها و کلاس ها / برچسب ­ها  ایجاد می­ شوند.

print("[INFO] loading images...")
imagePaths = list(paths.list_images(args["dataset"]))
data = []
labels = []

در قدم بعدی روی مسیرهای تصاویر حلقه ­ای ایجاد می­ شود که در هر دور حلقه اولا برچسب کلاس از نام فایل استخراج می­ شود، دوما تصویر با تقسیم به قطعات ۲۲۴ * ۲۲۴ پیکسلی پیش ­پردازش می­ شود که شبکه عصبی را تغذیه می­ کنند. سوما تصاویر به آرایه ­ها تبدیل می­ شوند و تصویر به عنوان ورودی به تابع preprocess_input داده می­ شود که به معنای متناسب بودن تصویر با قالبی است که مدل نیاز دارد (به بیان دیگر تضمین می­ شود که تصاویر بارگذاری شده با preprocess_input سازگار هستند) . بعد از اتمام حلقه، داده و برچسب ­ها برای پردازش ­های آتی به آرایه ­های NumPy  تبدیل می­ شوند و در نهایت برچسب ­ها با روش کدگذاری وان هات ( One Hot ) کدنویسی می ­شوند تا داده ­های طبقه ­بندی را به داده ­های عددی تبدیل کند.

for imagePath in imagePaths:
  label = imagePath.split(os.path.sep)[-2]

    image = load_img(imagePath, target_size=(224, 224))
  image = img_to_array(image)
  image = preprocess_input(image)

  # update the data and labels lists, respectively
  data.append(image)
  labels.append(label)

# convert the data and labels to NumPy arrays
data = np.array(data, dtype="float32")
labels = np.array(labels)

# perform one-hot encoding on the labels
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)

حال یک آزمایش در نپتیون ایجاد شده و یک نام  به این آزمایش تخصیص داده می­ شود و ابر پارامتر ها یاد داشت می­ شوند. جهت پاکسازی به صورت خودکار بعد از اتمام آزمایش ، توصیه می شود درصورت امکان هر چیزی در عبارت with قرار گیرد. قبل از آموزش مدل لازم است که داده به داده­ های آموزشی و داده ­های آزمایشی تقسیم شود. در این پروژه نزدیک به ۸۰ درصد داده ­ها به عنوان داده ­های آموزشی و ۲۰ درصد داده ­ها به عنوان داده ­های آزمایشی در نظر گرفته می ­شوند.  

with neptune.create_experiment(name=RUN_NAME, params=PARAMS):

  # partition the data into training and testing splits using 75% of
  # the data for training and the remaining 25% for testing
  (trainX, testX, trainY, testY) = train_test_split(data, labels,
                                                  	test_size=0.20, stratify=labels, random_state=42)

جهت افزایش داده، مولد برای تصویر آموزشی­ ساخته می­ شود. این مولد به طور مصنوعی اندازه­ ی مجموعه داده ­ی آموزشی را از طریق ایجاد نسخه­ های مختلف برای تصویر ، افزایش می ­دهد. این افزایش داده به مدل کمک می ­کند تا بهتر تعمیم داده شود.

aug = ImageDataGenerator(
  rotation_range=20,
  zoom_range=0.15,
  width_shift_range=0.2,
  height_shift_range=0.2,
  shear_range=0.15,
  horizontal_flip=True,
  fill_mode="nearest")

ساخت مدل

بعد از پیش ­پرداش داده و برچسب­ گذاری درست آن، گام بعدی آموزش یک مدل است تا تصاویر را با دقت طبقه ­بندی کند. دو راه برای این مهم وجود دارد. هم می­توان یک مدل طبقه ­بندی ­کننده را از صفر ایجاد کرد و  هم می­ توان از مدل ­های از پیش آموزش داده شده بهره برد. در این آموزش روش دوم  انتخاب می­ شود و مدل mobilinet_v2 که یک شبکه عصبی کانولوشنی با عمق ۵۳ لایه است، استفاده می­ شود.

معماری مدل Mobilenet V2
معماری مدل Mobilenet V2

نکته: وقتی از یک مدل از پیش آموزش­ داده ­شده استفاده می­ شود، انجام مطالعه ­ی کامل روی مدل مورد استفاده بسیار مهم است. مدل برای حل مسئله­ ی در دست بررسی باید قابل تطابق با مسئله باشد و باید قادر به کار کردن با مجموعه داده­ ی از پیش ­پردازش شده باشد. در این پروژه،  mobilinet_v2 اقتباس شده است زیرا در زمینه ­ی کارایی ­های مرتبط با تشخیص اشیا ، کاهش پیچیدگی و کاهش محدودیت­ های محاسباتی، پردازش گرافیکی و ذخیره­ سازی از فناوری های پیش رفته و فوق العاده روز به شمار می آید.

وقتی از یک مدل اقتباسی در پروژه استفاده شود باید مدل با وزن های از پیش ­آموزش داده شده بارگذاری شود و ساختار های بیشتری به مدل اضافه شود. از جمله ساختار هایی که به دنبال لایه­ های کانولوشنی به مدل اضافه می­ شوند، توابع فعال­سازی ReLU ( برای اضافه کردن ویژگی غیر­خطی بودن ) و Max Pooling ( برای کاهش نگاشت ویژگی ) هستند. به منظور اجتناب از  بیش­ برازش در شبکه ­های عصبی ، Dropout نیز اضافه می ­شود. سپس لایه­ های تماما متصل در انتها اضافه می ­شوند. در آخر تابع  هزینه ، بهینه ­ساز و سنجه ­ها به مدل مذکور اضافه می ­شوند. تابع هزینه جهت یافتن خطا ها و انحرافات در فرآیند آموزش استفاده می ­شود. Keras نیازمند تابع هزینه طی فرآیند کامپایل کردن مدل است. بهینه­ سازی فرآیند بسیار مهمی است که  وزن­ های ورودی را از طریق مقاسه­ ی تابع هزینه و پیش­ بینی و سنجه ­هایی که برای ارزیابی کارایی مدل استفاده می شوند ، بهبود می ­بخشد. مدل سریالایز شده و روی دیسک محلی ذخیره می­ شود.

نکته: مدل واقعی که آموزش داده می­ شود، مدل اصلی ( headModel ) است که روی مدل پایه ( baseModel ) قرار گرفته است.

baseModel = MobileNetV2(weights="imagenet", include_top=False,
                    	input_tensor=Input(shape=(224, 224, 3)))

headModel = baseModel.output
headModel = AveragePooling2D(pool_size=(7, 7))(headModel)
headModel = Flatten(name="flatten")(headModel)
headModel = Dense(128, activation="relu")(headModel)
headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel)
model = Model(inputs=baseModel.input, outputs=headModel)

# loop over all layers in the base model and freeze them so they will
# *not* be updated during the first training process
for layer in baseModel.layers:
  layer.trainable = False

# compile our model
print("[INFO] compiling model...")
opt = Adam(lr=PARAMS['INIT_LR'],
           decay=PARAMS['INIT_LR'] / PARAMS['EPOCHS'])
model.compile(loss="binary_crossentropy", optimizer=opt,
              metrics=["accuracy"])

# train the head of the network
print("[INFO] training head...")
tensorboard=tf.keras.callbacks.TensorBoard(log_dir=EXPERIMENT_LOG_DIR)
H = model.fit(
    	aug.flow(trainX, trainY, batch_size=PARAMS['BS']),
    	steps_per_epoch=len(trainX) // PARAMS['BS'],
    	validation_data=(testX, testY),
    	validation_steps=len(testX) // PARAMS['BS'],
    	epochs=PARAMS['EPOCHS'],
    	callbacks=[tensorboard]
  )

 قدم بعدی ارزیابی مدل توسط پیش ­بینی برچسب داده ­های آزمایشی است.

print("[INFO] evaluating network...")
predIdxs = model.predict(testX, batch_size=PARAMS['BS'])

# for each image in the testing set we need to find the index of the
# label with corresponding largest predicted probability
predIdxs = np.argmax(predIdxs, axis=1)

# show a nicely formatted classification report
print(classification_report(testY.argmax(axis=1), predIdxs,
                            target_names=lb.classes_))

# serialize the model to disk
print("[INFO] saving mask detector model...")
model.save(args["model"], save_format="h5")

در انتها لازم است دقت و هزینه ­ی آموزش از طریق داشبورد آزمایش نپتیون بصری ­سازی شوند. برای مشاهده داشبورد به آدرس https://ui.neptune.ai/codebrain/Drone/e/DRONE-13/charts رجوع شود.

داشبورد آزمایش هوش مصنوعی نپتیون

همچنین می ­توان میزان مصرف سخت ­افزار را از طریق داشبورد آزمایش نپتیون نظارت کرد. برای مشاهده این امر به آدرس https://ui.neptune.ai/codebrain/Drone/e/DRONE-13/monitoring  رجوع شود.

سخت افزار آزمایش هوش مصنوعی نپتیون

پیاده ­سازی مدل روی یک جریان ویدیو

برای پیاده­ سازی مدل روی یک جریان ویدیو، مدل ذخیره ­شده از قسمت قبلی بارگذاری خواهد شد. جهت شروع پیاده ­سازی، لازم است تعدادی کتابخانه ­ی ضروری وارد پروژه شوند. این کتابخانه­ ها و دستوری که آن ها را به پروژه می­ افزاید، در زیر نشان داده شده ­اند.

from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
from imutils.video import VideoStream
import numpy as np
import argparse
import imutils
import time
import cv2
import os
import time

سپس دو تابع به نام­ های  get_facenet_masknet   و   detect_and_predict_mask ایجاد می ­شود. تابع اول مدل آموزش ­دیده ­ی سریالایز شده قبلی و وزن­ های متناظرش را می ­خواند.

def get_facenet_masknet():
  # construct the argument parser and parse the arguments
  ap = argparse.ArgumentParser()
  ap.add_argument("-f", "--face", type=str,
                	default="face_detector",
                	help="path to face detector model directory")
  ap.add_argument("-m", "--model", type=str,
                	default="mask_detector.model",
                	help="path to trained face mask detector model")
  ap.add_argument("-c", "--confidence", type=float, default=0.5,
                	help="minimum probability to filter weak detections")
  args = vars(ap.parse_args())

  # load our serialized face detector model from disk
  print("[INFO] loading face detector model...")
  # prototxtPath = os.path.sep.join([args["face"],  "deploy.prototxt"])
  prototxtPath = (
    	'/Users/USER/Documents/DroneProjects/facemaskdetection/face_detector/deploy.prototxt')
  # weightsPath = os.path.sep.join([args["face"],
  #                             	"res10_300x300_ssd_iter_140000.caffemodel"])
  weightsPath = (
    	'/Users/USER/Documents/DroneProjects/facemaskdetection/face_detector/res10_300x300_ssd_iter_140000.caffemodel')
  faceNet = cv2.dnn.readNet(prototxtPath, weightsPath)

  # load the face mask detector model from disk
  print("[INFO] loading face mask detector model...")
  maskNet = load_model(
    	'/Users/USER/Documents/DroneProjects/facemaskdetection/mask_detector.model')
  return(faceNet, maskNet, args)

 تابع دوم ابعاد  فریم را می­ گیرد و یک توده از آن می­ سازد. این توده به درون شبکه عصبی فرستاده می­ شود تا چهره را تشخیص دهد. قطعه کد زیر چگونگی گرفتن ابعاد را نشان می­ دهد. 

def detect_and_predict_mask(frame, faceNet, maskNet, args):
(h, w) = frame.shape[:2]
  blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300),
                             	(۱۰۴٫۰, ۱۷۷٫۰, ۱۲۳٫۰))

  faceNet.setInput(blob)
  detections = faceNet.forward()

به دنبال تعریف این تابع، لیست چهره­ ها و مکان­ های متناظر آن ها  و همچنین لیست پیش­ بینی ­ها از شبکه ­ی ماسک صورت راه ­اندازی اولیه می­ شوند.

faces = []
locs = []
preds = []

 سپس روی تشخیص ­ها، حلقه ­ای ایجاد می ­شود و  احتمال مرتبط با هر تشخیص معین می­ شود. 

for i in range(0, detections.shape[2]):
  		confidence = detections[0, 0, i, 2]

در گام بعد احتمال به ­دست ­آمده از هر تشخیص با حداقل احتمال مقایسه می­ شوند و تشخیص­ هایی که احتمال آن ها از حداقل احتمال کوچک تر باشد به عنوان تشخیص ضعیف شناخته شده و کنار گذاشته می­ شوند.

	if confidence > args["confidence"]:
     	# compute the (x, y)-coordinates of the bounding box for
     	# the object
     	box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
     	(startX, startY, endX, endY) = box.astype("int")

همچنین باید از قرار گرفتن کادر محصور کننده درون ابعاد فریم اطمینان حاصل کرد.

	(startX, startY) = (max(0, startX), max(0, startY))
	(endX, endY) = (min(w - 1, endX), min(h - 1, endY))

همچنین تعدادی گام­ پیش­ پردازش اجرا می ­شوند تا منطقه ­ی مورد نظر چهره را استخراج کنند و سپس آن را از ترتیب کانال BGR به RGB تبدیل کنند. سپس  با تغییر اندازه قاب به ابعاد ۲۲۴ *۲۲۴  ادمه می­ دهد و آن را به آرایه­ ها تبدیل می کند.

	face = frame[startY:endY, startX:endX]
	face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
	face = cv2.resize(face, (224, 224))
	face = img_to_array(face)
	face = preprocess_input(face)

 همچنین باید از اضافه شدن صورت و جعبه ­های لبه ­ای به لیست متناظر­شان اطمینان حاصل شود.

 	faces.append(face)
	locs.append((startX, startY, endX, endY))

پیش ­بینی زمانی ایجاد می­ شود که حداقل یک صورت تشخیص داده شود سپس به جای پیش ­بینی یک به یک چهره ­ها،  یک پیش­ بینی دسته ­ای روی تمام صورت ­ها در یک لحظه از زمان انجام می­ گیرد.

if len(faces) > 0:
  	faces = np.array(faces, dtype="float32")
  	preds = maskNet.predict(faces, batch_size=32)

در انتها یک خروجی دو بعدی از مکان ­چهره ها و مکان متناظر آن ها برگردانده می ­شود.

return (locs, preds)

نتیجه­ گیری

 در این آموزش ، چگونگی پیش ­پردازش و بارگذاری مجموعه داده­ ی ماسک صورت و همچنین آموزش مدل تشخیص ماسک صورت با استفاده از تنسورفلو  Mobilenet V2 با پایتون شرح داده شد. مدل آموزش داده شده بعدا برای پیاده ­سازی جریان ویدیو اقتباس شد و در انتها مدل برای فاز بعدی پروژه سریالایز شد که نیازمند استقرار این سیستم نظارتی روی یک اپلیکیشن است. با دانش کسب­ شده از بخش یک و دو ، اکنون چارچوبی برای سیستم نظارتی مد نظر ایجاد کرده ایم. بخش بعدی نحوه­ ی استقرار این سیستم را بررسی می کنیم.

بیشتر بخوانید :

درباره‌ی احمدرضا جعفری

همچنین ببینید

انقلاب صنعتی چهارم اقتصاد خودمختار

انقلاب چهارم صنعتی : ظهور اقتصاد خودمختار – قسمت سوم

در قسمت قبل ، به بررسی سه شاخه اصلی انقلاب صنعتی چهارم یعنی اینترنت اشیاء …

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *