על מנת להבין את הפוסט הזה – יש צורך בהכרות מוקדמת עם רספברי פיי. אני מניח שאתם יודעים מה זה רספברי פיי (פוסט התחלתי פה) ואיך להתחבר אליו עם SSH. במדריך הזה אנו נשתמש במודל למידת מכונה שנקרא Tensorflow lite כדי לזהות אובייקטים שונים באמצעות תמונה. התמונה יכולה להגיע ממצלמה שמחוברת לרספברי פיי או מצלמה אחרת או אפילו מהאינטרנט.
העולם של AI ו-IoT או יותר נכון AIoT הוא עולם מרתק ומלא באפשרויות. במיוחד למייקרים – כלומר אלו שבונים מכשירים לאו דווקא באופן מקצועי אלא כתחביב. למרות שמאד קל להגיע ליישומים מקצועיים ושימושיים ולהרחיב את הידע לאופקים חדשים תוך עיסוק כזה. הפעם אני מסביר על tensorflow lite. חבילת תוכנה המאפשרת לנו להריץ מודלים של Machine Learning על גבי מכונות חלשות יחסית כמו רספברי פיי 5.
את הדוגמאות אני מריץ עם רספברי פיי 5 אבל אין בעיה להריץ אותם על רספברי פיי 4 או אפילו 3 שאליהם אנו מתחברים ב-SSH. אני אחפור פה קצת על Tensorflow ומי שרוצה את המדריך בשביל הת׳כלס יכול לדלג אל תת הכותרת התקנה.
TensorFlow, שפותחה על ידי גוגל, היא חבילת קוד פתוח המאפשרת למפתחים ליצור מודלים של למידת מכונה ממש בקלות. היא תומכת במגוון רחב של משימות עם כל הבאזז וורדס המתאימות של רשתות נוירונים, עיבוד שפה טבעית, ראייה ממוחשבת ועוד. בגדול לא צריך ממש לדעת איך זה עובד על מנת להשתמש בזה לבניית אפליקציות מרשימות במיוחד. למרות שמי שכן מתעניין יכול בהחלט ללמוד ממגוון המקורות שיש ברשת. מהניסיון שלי – דווקא העיסוק בזה יכול ממש לסקרן גם מתכנתים מנוסים.
מה שחשוב להכיר הוא את המושג מודל שהוא מושג ליבה ליבה של למידת מכונה. זהו ייצוג מתמטי של מערכת או תהליך שאנו מנסים להבין או לחזות. המודל מקבל קלט (נתונים) ומייצר פלט (תחזית או החלטה) בהתבסס על פרמטרים שהוא למד. למדנו על מודל פשוט מאד כזה בפוסט אחר שכתבתי על רגרסיה ליניארית ואפשר לבקר בו.
אבל לעניין שלנו – מודל לזיהוי אובייקטים יקבל תמונה כקלט ויחזיר לנו את האובייקטים המתאימים המופיעים בה (כמו "חתול" או "כלב"). בשביל זה אנחנו צריכים מודל שמזהה אובייקטים וגם כמובן התקנה של TF lite.
התקנה של Tflite על רספברי פיי
נתחיל בהתקנה. ראשית, נשדרג את מערכת ההפעלה שלנו ושאר החבילות עם:
sudo apt-get update
sudo apt-get install -y
זה תמיד הדבר הכי חשוב שהכי כדאי לעשות לפני כל מדריך. זה יכול לקחת זמן אבל זה שווה את זה כי מהניסיון שלי יש כאבים רבים שנפתרים כשמקפידים לעשות את זה.
אחרי שהכל מוכן – נעשה reboot עם sudo reboot ונתחיל בהתקנת Tensorflow lite.
אנו ניצור תיקיה בשם lite-tf תחת תיקית הבית שלנו ונתקין שם את tflite. אם אתם לא מבינים כלום בפייתון זה גם בסדר – פשוט העתיקו את הקוד הבא איך שהוא ותריצו אותו.
mkdir ~/lite-tf && cd ~/lite-tf && python3 -m venv venv && source venv/bin/activate && pip install tflite-runtime "numpy<2" tflite_support
הקוד הזה יוצר תיקית python-tf עם כל התלויות הנדרשות והסביבה הוירטואלית של פייתון (זה לא מחשב וירטואלי אלא כינוי לסביבה שבה התלויות של פייתון יכולות לרוץ).
שימו לב: אני מתקין את numpy בגרסה פחות מ-2 כי כרגע tflow lite לא תומך בה. יש סיכוי שבעתיד אני אצטרך לתקן את זה. אם יש לכם איזושהי שגיאה שמזכירה את numpy, נסו להקליד pip install numpy ולראות אם זה נפתור. נכון ל-21 לספטמבר 2024 זה עובד.
הפעלה בסביבה הוירטואלית
חשוב מאד לרוץ בקונטקסט של הסביבה הזו. מפעילים אותה כאשר נמצאים בתיקית lite-tf ומקלידים source venv/bin/activate. בלי זה – קוד הפייתון לא יעבוד! אנחנו יודעים שאנחנו בתוך הסביבה הוירטואלית אם יש את השם שלה בסוגריים כשאנו מפעילים את ה-SSH. למי שלא יודע פייתון זה יראה מוזר (וזה בסדר גמור) אפשר ללמוד על כך עוד בפוסט על pip + venv. או לא ללמוד על כך אבל להקפיד להיות בתוך הסביבה ולראות משהו כזה:
חלק גדול מהדוגמאות מראות איך אנחנו מתחברים למצלמת רספברי פיי ועושים דברים משוגעים. אני אדגים דווקא עם דוגמה פשוטה יותר כדי להבהיר כמה זה קל ואיך אפשר לעבוד עם מודלים שונים.
בחירת מודל
נתחיל בעבודה עם מודל לזיהוי תמונה. יש המווווןןןןן מודלים שאפשר להשתמש בהם. גם ב-Tflite. אנו נשתמש ב-Kaggle כדי להוריד אותם. ניכנס לאתר Kaggle ונחפש efficientdet. נגלה שיש משפחה של מודלים כאלו לזיהוי אובייקטים. נלחץ על לשונית Tflite
נראה שאנחנו יכולים להוריד מודלים. אנו נוריד את המודל למחשב שלנו ונעביר לרספברי פיי (זה ממש קל עם אינטגרציה ל Visual Studio Code כפי שכתבתי בפוסט הזה) או ישירות באמצעות curl. הקובץ הוא עם סיומת tflite
עם המודל הזה אנחנו יכולים להעביר תמונה ולקבל מידע על מה שיש בו. אנו נכתוב קוד פשוט מאד שמעביר תמונה ואז אנו מקבלים על מה שנמצא שם. המודל הזה עובד עם קטגוריות ואנו מקבלים מספרי קטגוריות שאותם צריך להמיר לשמות. השמות של הקטגוריות הן לפי הסטנדרט של COCO.
אנו נבדוק את התמונה הזו שאני צילמתי מהמרפסת שלי. היא תמונה חופשית לשימוש:
אפשר להשתמש בכל תמונה שהיא כמובן ואפילו בוידאו. אבל אנו נעתיק את התמונה הזו לרספברי פיי ונכתוב main.py באופן הזה:
import tflite_runtime.interpreter as tflite
import numpy as np
from PIL import Image
# Correct COCO labels
COCO_LABELS = [
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
"potted plant", "bed", "dining table", "toilet", "TV", "laptop", "mouse", "remote", "keyboard", "cell phone",
"microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
"hair drier", "toothbrush"
]
# Load the TensorFlow Lite model and allocate tensors
model_path = "lite-model_efficientdet_lite0_detection_metadata_1.tflite" # Update with your TFLite model path
interpreter = tflite.Interpreter(model_path=model_path)
interpreter.allocate_tensors()
# Get input and output tensor details
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# Load and preprocess the image
def preprocess_image(image_path):
img = Image.open(image_path).convert("RGB").resize((320, 320))
input_data = np.expand_dims(np.array(img), axis=0).astype(np.uint8)
return input_data
# Run inference and return the detected objects
def run_inference(image_path):
input_data = preprocess_image(image_path)
# Set input tensor and run inference
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
# Extract output tensors (bounding boxes, class IDs, and scores)
boxes = interpreter.get_tensor(output_details[0]['index'])[0] # Bounding boxes
class_ids = interpreter.get_tensor(output_details[1]['index'])[0] # Class IDs
scores = interpreter.get_tensor(output_details[2]['index'])[0] # Confidence scores
return boxes, class_ids, scores
# Detect and print objects using predefined COCO labels
def detect_objects(image_path):
boxes, class_ids, scores = run_inference(image_path)
print(f"Detection Results for '{image_path}':")
for i in range(len(scores)):
if scores[i] > 0.5: # Confidence threshold
class_id = int(class_ids[i]) # COCO class IDs start from 1
score = scores[i]
# Class IDs in COCO are 1-based, so subtract 1 to map to 0-indexed Python list
print(f"Detected {COCO_LABELS[class_id - 1]} with confidence {score:.2f}")
# Input image path
image_path = "picture-image.jpeg" # Replace with your image
# Run object detection
detect_objects(image_path)
הקוד הזה ג׳ונרט על ידי ChatGPT 4o עם קצת הכוונה שלי והשארתי את ההערות המקוריות. הוא פשוט, אבל יש כמה דברים חשובים שיש בו:
הראשון הוא הגדרת ה-Labels. אפשר לקרוא אותם באופן יותר אלגנטי אבל בגדול כאן אני משתמש בהגדרה קשיחה שלהם כי המודל מוציא לי מספרים.
השני הוא המרת התמונה לגודל. כל מודל עובד עם תמונה אחרת. אני המרתי פה את התמונה ל-320 על 320 שזה מה שמודל lite-model_efficientdet_lite0_detection_metadata_1.tflite דורש. למשל אם אני אשתמש ב-efficientdet-tflite-lite4-detection-metadata-v2.tflite, אני אצטרך להמיר אותו ל-640 על 640.
ועכשיו… נריץ את הקוד! זה הפלט:
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
Detection Results for 'picture-image.jpeg':
Detected bird with confidence 0.69
ועכשיו… אם אני רוצה, אני יכול לחבר את הרספברי פיי לזמזם למשל, או לתותח מים, או לכל דבר שיגרום לציפור לברוח משם!
אם תשחקו עם תמונות, יש סיכוי שתראו שהאבחון לא תמיד מדויק. אפשר להשתמש במודל יותר מתקדם כמו efficientdet-tflite-lite4-detection-metadata-v2.tflite. הנה הקוד שמריץ אותו שזהה חוץ משני פרמטרים – הראשון הוא שאני משנה את הגודל ל-640 על 640 והשני הוא ששם המודל כמובן שונה:
import tflite_runtime.interpreter as tflite
import numpy as np
from PIL import Image
# Correct COCO labels (starting from index 0 in Python list)
COCO_LABELS = [
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
"potted plant", "bed", "dining table", "toilet", "TV", "laptop", "mouse", "remote", "keyboard", "cell phone",
"microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
"hair drier", "toothbrush"
]
# Load the TensorFlow Lite model and allocate tensors
model_path = "efficientdet-tflite-lite4-detection-metadata-v2.tflite" # Update with your TFLite model path
interpreter = tflite.Interpreter(model_path=model_path)
interpreter.allocate_tensors()
# Get input and output tensor details
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# Load and preprocess the image (resize to 640x640 for EfficientDet Lite4)
def preprocess_image(image_path):
img = Image.open(image_path).convert("RGB").resize((640, 640))
input_data = np.expand_dims(np.array(img), axis=0).astype(np.uint8)
return input_data
# Run inference and return the detected objects
def run_inference(image_path):
input_data = preprocess_image(image_path)
# Set input tensor and run inference
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
# Extract output tensors (bounding boxes, class IDs, and scores)
boxes = interpreter.get_tensor(output_details[0]['index'])[0] # Bounding boxes
class_ids = interpreter.get_tensor(output_details[1]['index'])[0] # Class IDs
scores = interpreter.get_tensor(output_details[2]['index'])[0] # Confidence scores
return boxes, class_ids, scores
# Detect and print objects using predefined COCO labels
def detect_objects(image_path):
boxes, class_ids, scores = run_inference(image_path)
print(f"Detection Results for '{image_path}':")
for i in range(len(scores)):
if scores[i] > 0.5: # Confidence threshold
class_id = int(class_ids[i]) # COCO class IDs start from 1
score = scores[i]
# Class IDs in COCO are 1-based, so subtract 1 to map to 0-indexed Python list
print(f"Detected {COCO_LABELS[class_id - 1]} with confidence {score:.2f}")
# Input image path
image_path = "picture-image.jpeg" # Replace with your image
# Run object detection
detect_objects(image_path)
שימו לב שאם אתם משתמשים ברספברי פיי ישן, החישוב של זיהוי התמונה יקח זמן ואפילו הרבה זמן. זו הסיבה שאנחנו צריכים לפעמים חומרה חזקה.
סיכום
מה שחשוב הוא שפה אנחנו משתמשים ממש בקוד של ברזלים. לא קוד דמו מוכן שמצייר לי מסגרות סביב האובייקט. מפה אני יכול להפעיל את ה-GPIO כדי להפעיל כל מיני דברים או התראות. השמים הם הגבול! את הקלט אגב, אני יכול לייצר באמצעות מצלמה של רספברי פיי או מיקרו בקר ששולח את התמונה אלי. הפוסט על חיבור מצלמה לרספברי פיי או תקשורת של ESP32 עם MQTT יסייעו אם אתם רוצים לקחת את זה קדימה.
כמובן שמה שטוב פה הוא שאנחנו יכולים גם לאמן מודל משלנו! בפוסט הבא ניקח את זה הלאה ונאמן מודל משלנו. הפעם – לסיווג תמונות.
חשוב לציין שמסיבה מסוימת החליטו לשנות את השם של Tensorflow lite ל-Litert. כרגע הרוב המוחץ של הדוקומנטציה הוא על Tensorflow lite או TFlite. עוד כמה שנים יש סיכוי שנראה יותר litert.