إنشاء مخرجات PaliGemma باستخدام Keras

عرض على ai.google.dev التشغيل في Google Colab الفتح في Vertex AI عرض المصدر على GitHub

تتضمّن نماذج PaliGemma إمكانات متعددة الوسائط، ما يتيح لك إنشاء نتائج باستخدام بيانات الإدخال النصية والمرئية. يمكنك استخدام بيانات الصور مع هذه النماذج لتوفير سياق إضافي لطلباتك، أو استخدام النموذج لتحليل محتوى الصور. يوضّح لك هذا البرنامج التعليمي كيفية استخدام PaliGemma مع Keras لتحليل الصور والإجابة عن أسئلة حولها.

محتوى دفتر الملاحظات هذا

تستخدم هذه المفكرة PaliGemma مع Keras وتوضّح لك كيفية:

  • تثبيت Keras والموارد التابعة المطلوبة
  • نزِّل PaliGemmaCausalLM، وهو إصدار من PaliGemma مدرَّب مسبقًا لنمذجة اللغة المرئية السببية، واستخدِمه لإنشاء نموذج.
  • اختبار قدرة النموذج على استنتاج معلومات حول الصور المقدَّمة

قبل البدء

قبل الاطّلاع على دفتر الملاحظات هذا، يجب أن تكون على دراية برمز Python، بالإضافة إلى كيفية تدريب النماذج اللغوية الكبيرة (LLM). ليس عليك أن تكون على دراية بـ Keras، ولكن من المفيد أن تكون لديك معرفة أساسية بها عند قراءة نموذج الرمز.

الإعداد

توضّح الأقسام التالية الخطوات التمهيدية للحصول على دفتر ملاحظات لاستخدام نموذج PaliGemma، بما في ذلك الوصول إلى النموذج والحصول على مفتاح واجهة برمجة التطبيقات وإعداد وقت تشغيل دفتر الملاحظات.

الحصول على إذن بالوصول إلى PaliGemma

قبل استخدام PaliGemma للمرة الأولى، يجب طلب الوصول إلى النموذج من خلال Kaggle باتّباع الخطوات التالية:

  1. سجِّل الدخول إلى Kaggle أو أنشئ حسابًا جديدًا على Kaggle إذا لم يكن لديك حساب.
  2. انتقِل إلى بطاقة نموذج PaliGemma وانقر على طلب الوصول.
  3. أكمِل نموذج الموافقة واقبَل الأحكام والشروط.

ضبط مفتاح واجهة برمجة التطبيقات

لاستخدام PaliGemma، يجب تقديم اسم المستخدم على Kaggle ومفتاح واجهة برمجة التطبيقات على Kaggle.

لإنشاء مفتاح واجهة برمجة تطبيقات Kaggle، افتح صفحة الإعدادات في Kaggle وانقر على إنشاء رمز مميّز جديد. سيؤدي ذلك إلى بدء تنزيل ملف kaggle.json يحتوي على بيانات اعتماد واجهة برمجة التطبيقات.

بعد ذلك، في Colab، انقر على الأسرار (🔑) في اللوحة اليمنى وأضِف اسم مستخدم Kaggle ومفتاح واجهة برمجة التطبيقات في Kaggle. احفظ اسم المستخدم تحت الاسم KAGGLE_USERNAME ومفتاح واجهة برمجة التطبيقات تحت الاسم KAGGLE_KEY.

اختيار وقت التشغيل

لإكمال هذا البرنامج التعليمي، يجب أن يكون لديك وقت تشغيل Colab مع موارد كافية لتشغيل نموذج PaliGemma. في هذه الحالة، يمكنك استخدام وحدة معالجة الرسومات T4:

  1. في أعلى يسار نافذة Colab، انقر على القائمة المنسدلة ▾ (خيارات ربط إضافية).
  2. اختَر تغيير نوع بيئة التشغيل.
  3. ضمن مسرِّع الأجهزة، اختَر وحدة معالجة الرسومات T4.

ضبط متغيرات البيئة

اضبط متغيّرات البيئة لكلّ من KAGGLE_USERNAME وKAGGLE_KEY وKERAS_BACKEND.

import os
from google.colab import userdata

# Set up environmental variables
os.environ["KAGGLE_USERNAME"] = userdata.get('KAGGLE_USERNAME')
os.environ["KAGGLE_KEY"] = userdata.get('KAGGLE_KEY')
os.environ["KERAS_BACKEND"] = "jax"

تثبيت Keras

نفِّذ الخلية أدناه لتثبيت Keras.

pip install -U -q keras-nlp keras-hub kagglehub

استيراد الموارد التابعة وإعداد Keras

ثبِّت العناصر التابعة اللازمة لهذا الدفتر وخصِّص الخلفية في Keras. ستضبط أيضًا Keras لاستخدام bfloat16 حتى يستخدم إطار العمل مساحة ذاكرة أقل.

import keras
import keras_hub
import numpy as np
import PIL
import requests
import io
import matplotlib
import re
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image

keras.config.set_floatx("bfloat16")

تحميل النموذج

بعد الانتهاء من إعداد كل شيء، يمكنك تنزيل النموذج المدرَّب مسبقًا وإنشاء بعض الطرق المساعدة التي تساعد النموذج في إنشاء ردوده. في هذه الخطوة، يمكنك تنزيل نموذج باستخدام PaliGemmaCausalLM من Keras Hub. تساعدك هذه الفئة في إدارة بنية نموذج اللغة المرئية السببية الخاص بـ PaliGemma وتشغيله. يتوقّع نموذج اللغة المرئية السببية الرمز المميز التالي استنادًا إلى الرموز المميزة السابقة. يوفّر Keras Hub عمليات تنفيذ للعديد من تصاميم النماذج الشائعة.

أنشئ النموذج باستخدام طريقة from_preset واطبع ملخّصه. سيستغرق إكمال هذه العملية دقيقة واحدة تقريبًا.

paligemma = keras_hub.models.PaliGemmaCausalLM.from_preset("kaggle://keras/paligemma2/keras/pali_gemma2_mix_3b_224")
paligemma.summary()

إنشاء طرق مساعدة

لمساعدتك في إنشاء ردود من النموذج، أنشئ طريقتَين مساعدتَين:

  • crop_and_resize: طريقة مساعدة لـ read_img. تؤدي هذه الطريقة إلى اقتصاص الصورة وتغيير حجمها إلى الحجم الذي تم تمريره حتى يتم تغيير حجم الصورة النهائية بدون تشويه نسب الصورة.
  • read_img: طريقة مساعدة لـ read_img_from_url. هذه الطريقة هي التي تفتح الصورة فعليًا، وتعيد ضبط حجمها ليتناسب مع قيود النموذج، وتضعها في مصفوفة يمكن للنموذج تفسيرها.
  • read_img_from_url: تأخذ صورة من خلال عنوان URL صالح. تحتاج إلى هذه الطريقة لتمرير الصورة إلى النموذج.

ستستخدم read_img_from_url في الخطوة التالية من دفتر الملاحظات هذا.

def crop_and_resize(image, target_size):
    width, height = image.size
    source_size = min(image.size)
    left = width // 2 - source_size // 2
    top = height // 2 - source_size // 2
    right, bottom = left + source_size, top + source_size
    return image.resize(target_size, box=(left, top, right, bottom))

def read_image(url, target_size):
    contents = io.BytesIO(requests.get(url).content)
    image = PIL.Image.open(contents)
    image = crop_and_resize(image, target_size)
    image = np.array(image)
    # Remove alpha channel if necessary.
    if image.shape[2] == 4:
        image = image[:, :, :3]
    return image

def parse_bbox_and_labels(detokenized_output: str):
  matches = re.finditer(
      '<loc(?P<y0>\d\d\d\d)><loc(?P<x0>\d\d\d\d)><loc(?P<y1>\d\d\d\d)><loc(?P<x1>\d\d\d\d)>'
      ' (?P<label>.+?)( ;|$)',
      detokenized_output,
  )
  labels, boxes = [], []
  fmt = lambda x: float(x) / 1024.0
  for m in matches:
    d = m.groupdict()
    boxes.append([fmt(d['y0']), fmt(d['x0']), fmt(d['y1']), fmt(d['x1'])])
    labels.append(d['label'])
  return np.array(boxes), np.array(labels)

def display_boxes(image, boxes, labels, target_image_size):
  h, l = target_size
  fig, ax = plt.subplots()
  ax.imshow(image)
  for i in range(boxes.shape[0]):
      y, x, y2, x2 = (boxes[i]*h)
      width = x2 - x
      height = y2 - y
      # Create a Rectangle patch
      rect = patches.Rectangle((x, y),
                               width,
                               height,
                               linewidth=1,
                               edgecolor='r',
                               facecolor='none')
      # Add label
      plt.text(x, y, labels[i], color='red', fontsize=12)
      # Add the patch to the Axes
      ax.add_patch(rect)

  plt.show()

def display_segment_output(image, bounding_box, segment_mask, target_image_size):
    # Initialize a full mask with the target size
    full_mask = np.zeros(target_image_size, dtype=np.uint8)
    target_width, target_height = target_image_size

    for bbox, mask in zip(bounding_box, segment_mask):
        y1, x1, y2, x2 = bbox
        x1 = int(x1 * target_width)
        y1 = int(y1 * target_height)
        x2 = int(x2 * target_width)
        y2 = int(y2 * target_height)

        # Ensure mask is 2D before converting to Image
        if mask.ndim == 3:
            mask = mask.squeeze(axis=-1)
        mask = Image.fromarray(mask)
        mask = mask.resize((x2 - x1, y2 - y1), resample=Image.NEAREST)
        mask = np.array(mask)
        binary_mask = (mask > 0.5).astype(np.uint8)


        # Place the binary mask onto the full mask
        full_mask[y1:y2, x1:x2] = np.maximum(full_mask[y1:y2, x1:x2], binary_mask)
    cmap = plt.get_cmap('jet')
    colored_mask = cmap(full_mask / 1.0)
    colored_mask = (colored_mask[:, :, :3] * 255).astype(np.uint8)
    if isinstance(image, Image.Image):
        image = np.array(image)
    blended_image = image.copy()
    mask_indices = full_mask > 0
    alpha = 0.5

    for c in range(3):
        blended_image[:, :, c] = np.where(mask_indices,
                                          (1 - alpha) * image[:, :, c] + alpha * colored_mask[:, :, c],
                                          image[:, :, c])

    fig, ax = plt.subplots()
    ax.imshow(blended_image)
    plt.show()

إنشاء الناتج

بعد تحميل النموذج وإنشاء طرق مساعدة، يمكنك تقديم طلب إلى النموذج يتضمّن بيانات صور ونصوص لإنشاء ردود. يتم تدريب نماذج PaliGemma على بنية طلبات محدّدة لأداء مهام معيّنة، مثل answer وcaption وdetect. لمزيد من المعلومات حول بناء جملة طلبات PaliGemma، يُرجى الاطّلاع على طلبات PaliGemma وتعليمات النظام.

جهِّز صورة لاستخدامها في طلب إنشاء من خلال استخدام الرمز التالي لتحميل صورة اختبارية في عنصر:

target_size = (224, 224)
image_url = 'https://storage.googleapis.com/keras-cv/models/paligemma/cow_beach_1.png'
cow_image = read_image(image_url, target_size)
matplotlib.pyplot.imshow(cow_image)

الإجابة بلغة معيّنة

يوضّح مثال الرمز التالي كيفية مطالبة نموذج PaliGemma بتقديم معلومات حول عنصر يظهر في صورة مقدَّمة. يستخدم هذا المثال بنية answer {lang} ويعرض أسئلة إضافية بلغات أخرى:

prompt = 'answer en where is the cow standing?\n'
# prompt = 'svar no hvor står kuen?\n'
# prompt = 'answer fr quelle couleur est le ciel?\n'
# prompt = 'responda pt qual a cor do animal?\n'

output = paligemma.generate(
    inputs={
        "images": cow_image,
        "prompts": prompt,
    }
)
print(output)

استخدام طلب detect

يستخدم نموذج الرمز البرمجي التالي بنية طلب detect لتحديد موقع عنصر في الصورة المقدَّمة. يستخدم الرمز الدالتَين parse_bbox_and_labels() وdisplay_boxes() اللتَين تم تحديدهما سابقًا لتفسير ناتج النموذج وعرض مربعات الإحاطة التي تم إنشاؤها.

prompt = 'detect cow\n'
output = paligemma.generate(
    inputs={
        "images": cow_image,
        "prompts": prompt,
    }
)
boxes, labels = parse_bbox_and_labels(output)
display_boxes(cow_image, boxes, labels, target_size)

استخدام طلب segment

يستخدم نموذج الرمز البرمجي التالي بنية طلب segment لتحديد مساحة الصورة التي يشغلها أحد العناصر. يستخدم مكتبة Google big_vision لتفسير نتائج النموذج وإنشاء قناع للعنصر المجزّأ.

قبل البدء، ثبِّت مكتبة big_vision والموارد التابعة لها، كما هو موضّح في مثال الرمز البرمجي التالي:

import os
import sys

# TPUs with
if "COLAB_TPU_ADDR" in os.environ:
  raise "It seems you are using Colab with remote TPUs which is not supported."

# Fetch big_vision repository if python doesn't know about it and install
# dependencies needed for this notebook.
if not os.path.exists("big_vision_repo"):
  !git clone --quiet --branch=main --depth=1 \
     https://github.com/google-research/big_vision big_vision_repo

# Append big_vision code to python import path
if "big_vision_repo" not in sys.path:
  sys.path.append("big_vision_repo")


# Install missing dependencies. Assume jax~=0.4.25 with GPU available.
!pip3 install -q "overrides" "ml_collections" "einops~=0.7" "sentencepiece"

بالنسبة إلى مثال التقسيم هذا، حمِّل صورة مختلفة تتضمّن قطة وجهِّزها.

cat = read_image('https://big-vision-paligemma.hf.space/file=examples/barsik.jpg', target_size)
matplotlib.pyplot.imshow(cat)

في ما يلي دالة للمساعدة في تحليل ناتج التقسيم من PaliGemma

import  big_vision.evaluators.proj.paligemma.transfers.segmentation as segeval
reconstruct_masks = segeval.get_reconstruct_masks('oi')
def parse_segments(detokenized_output: str) -> tuple[np.ndarray, np.ndarray]:
  matches = re.finditer(
      '<loc(?P<y0>\d\d\d\d)><loc(?P<x0>\d\d\d\d)><loc(?P<y1>\d\d\d\d)><loc(?P<x1>\d\d\d\d)>'
      + ''.join(f'<seg(?P<s{i}>\d\d\d)>' for i in range(16)),
      detokenized_output,
  )
  boxes, segs = [], []
  fmt_box = lambda x: float(x) / 1024.0
  for m in matches:
    d = m.groupdict()
    boxes.append([fmt_box(d['y0']), fmt_box(d['x0']), fmt_box(d['y1']), fmt_box(d['x1'])])
    segs.append([int(d[f's{i}']) for i in range(16)])
  return np.array(boxes), np.array(reconstruct_masks(np.array(segs)))

إرسال طلب إلى PaliGemma لتقسيم القطة في الصورة

prompt = 'segment cat\n'
output = paligemma.generate(
    inputs={
        "images": cat,
        "prompts": prompt,
    }
)

تصوُّر القناع الذي تم إنشاؤه من PaliGemma

bboxes, seg_masks = parse_segments(output)
display_segment_output(cat, bboxes, seg_masks, target_size)

الطلبات المجمّعة

يمكنك تقديم أكثر من أمر واحد ضمن طلب واحد كمجموعة من التعليمات. يوضّح المثال التالي كيفية تنظيم نص الطلب لتقديم تعليمات متعدّدة.

prompts = [
    'answer en where is the cow standing?\n',
    'answer en what color is the cow?\n',
    'describe en\n',
    'detect cow\n',
    'segment cow\n',
]
images = [cow_image, cow_image, cow_image, cow_image, cow_image]
outputs = paligemma.generate(
    inputs={
        "images": images,
        "prompts": prompts,
    }
)
for output in outputs:
    print(output)