使用小型視覺語言模型(VLM)進(jìn)行物體識(shí)別與計(jì)數(shù)
今天的重點(diǎn)是一個(gè)具有無數(shù)實(shí)際應(yīng)用的功能:在邊緣設(shè)備(如智能手機(jī)、物聯(lián)網(wǎng)設(shè)備和嵌入式系統(tǒng))上運(yùn)行小型視覺語言模型(VLM)。這些模型在識(shí)別和指出物體方面越來越出色。具體來說,它們?cè)跈z測(cè)制造缺陷、計(jì)數(shù)可用停車位或發(fā)現(xiàn)癌細(xì)胞方面表現(xiàn)優(yōu)異。盡管它們潛力巨大,但許多人并不知道這些小型VLM是專門為這些任務(wù)訓(xùn)練的。
模型:Molmo 7B
Molmo 是由 Allen Institute for AI 開發(fā)的一組開放視覺語言模型。它們?cè)?PixMo 數(shù)據(jù)集上進(jìn)行訓(xùn)練,該數(shù)據(jù)集包含 100 萬對(duì)圖像-文本對(duì)。基于 Qwen2–7B 和 OpenAI CLIP 構(gòu)建的 Molmo 7B-D 幾乎與 GPT-4V 和 GPT-4o 一樣出色。
工具:MLX-VLM 以及 MLX 社區(qū)
MLX-VLM 是 Prince Canuma(Blaizzy)開發(fā)的一個(gè)工具,用于在 Mac 上使用 MLX 運(yùn)行和微調(diào)視覺語言模型(VLM)。它支持多種模型,如 molmo、llava、llava_bunny、llava_next、mllama、multi_modality、paligemma、phi3_v、pixtral 和 qwen2_vl。這些模型可以在 Hugging Face 上的 MLX 社區(qū)中免費(fèi)下載。
Hugging Face 上的 MLX 社區(qū)是一個(gè)共享 Apple 的 MLX 框架預(yù)轉(zhuǎn)換模型權(quán)重的中心。它提供了適用于訓(xùn)練、微調(diào)和部署大型語言模型(LLM)和視覺模型的模型。流行的選項(xiàng)如用于語音識(shí)別的 Whisper 和用于圖像生成的 Stable Diffusion 也可用。用戶還可以通過上傳自己的模型或在其項(xiàng)目中使用 MLX 工具來做出貢獻(xiàn)。
我們的需求
要開始,我們需要設(shè)置一個(gè)虛擬環(huán)境并安裝所需的庫。以下是步驟列表:
(1) 創(chuàng)建并激活虛擬環(huán)境。
(2) 安裝必要的包:
pip install -U mlx-vlm
pip install einops
pip install torch torchvision
pip install matplotlib
我們將使用下圖來測(cè)試我們的工作流程。你可以替換圖像并調(diào)整提示以適應(yīng)不同的應(yīng)用。例如,你可以計(jì)算停車場(chǎng)中的汽車數(shù)量、人群中的人數(shù)或體育場(chǎng)中的空座位。
待識(shí)別的鋼管
在 MLX 中運(yùn)行 Molmo
在 MLX 中運(yùn)行這個(gè)模型非常簡(jiǎn)單。你可以復(fù)制并粘貼以下代碼行,然后就可以嘗試這個(gè)模型了。確保根據(jù)你的用例更改圖像路徑。對(duì)于我來說,我將保留 pipes_test.jpg,并在提示中簡(jiǎn)單地問:“指出圖像中的鋼管。”
from mlx_vlm import load, apply_chat_template, generate
from mlx_vlm.utils import load_image
import matplotlib.pyplot as plt
model, processor = load("mlx-community/Molmo-7B-D-0924-4bit",processor_config={"trust_remote_code": True})
config = model.config
image_path = "pipes_test.jpg"
image = load_image(image_path)
messages = [{"role": "user", "content": "Point the pipes in the images"}]
prompt = apply_chat_template(processor, config, messages)
output = generate(model, processor, image, prompt, max_tokens=1200, temperature=0.7)
print(output)
上述代碼片段的輸出如下:
<points x1="12.3" y1="76.8" x2="17.0" y2="63.9" x3="19.8" y3="49.0" x4="20.7" y4="80.6" x5="24.9" y5="66.7" x6="26.8" y6="50.8" x7="30.9" y7="84.8" x8="33.6" y8="70.2" x9="40.0" y9="88.3" alt="pipes in the images">pipes in the images</points>
這是模型被訓(xùn)練來響應(yīng)的方式。然而,為了驗(yàn)證這個(gè)輸出,我們需要進(jìn)行后處理并在圖像上繪制這些點(diǎn)。所以,讓我們來做吧!
在圖像中指出和檢測(cè)物體
讓我們實(shí)現(xiàn)兩個(gè)函數(shù):第一個(gè)用于解析點(diǎn)的坐標(biāo),第二個(gè)用于繪制它們。在解析點(diǎn)時(shí),重要的是要注意坐標(biāo)是基于圖像的寬度和高度進(jìn)行歸一化的。如下面的代碼片段所示,我們需要將歸一化的值除以 100,然后分別乘以圖像的寬度和高度。
def parse_points(points_str):
# Function was taken from https://github.com/Blaizzy/mlx-vlm
if isinstance(points_str, tuple):
return points_str
x_coords = []
y_coords = []
# Handle multi-point format
if 'x1="' in points_str:
i = 1
while True:
try:
x = float(points_str.split(f'x{i}="')[1].split('"')[0])
y = float(points_str.split(f'y{i}="')[1].split('"')[0])
x_coords.append(x)
y_coords.append(y)
i += 1
except IndexError:
break
elif 'x="' in points_str:
x = float(points_str.split('x="')[1].split('"')[0])
y = float(points_str.split('y="')[1].split('"')[0])
x_coords.append(x)
y_coords.append(y)
try:
labels = points_str.split('alt="')[1].split('">')[0].split(", ")
item_labels = labels
except IndexError:
item_labels = [f"Point {i+1}" for i in range(len(x_coords))]
return x_coords, y_coords, item_labels
現(xiàn)在讓我們使用 Matplotlib 在圖像上繪制點(diǎn)的位置。你也可以繪制標(biāo)簽,但在我的情況下,我只需要點(diǎn)和數(shù)字就夠了。
def plot_locations(points: str | tuple, image, point_size=10, font_size=12):
if isinstance(points, str):
x_coords, y_coords, item_labels = parse_points(points)
else:
x_coords, y_coords, item_labels = points
grayscale_image = image.convert("L")
img_width, img_height = grayscale_image.size
x_norm = [(x / 100) * img_width for x in x_coords]
y_norm = [(y / 100) * img_height for y in y_coords]
if len(item_labels) != len(x_norm):
item_labels *= len(x_norm)
plt.figure(figsize=(10, 8))
plt.imshow(grayscale_image, cmap="gray")
plt.axis("off")
for i, (x, y, label) in enumerate(zip(x_norm, y_norm, item_labels), start=1):
label_with_number = f"{i}"
plt.plot(x, y, "o", color="red", markersize=point_size, label=label_with_number)
plt.annotate(
label_with_number,
(x, y),
xytext=(0, 10),
textcoords="offset points",
ha="center",
color="red",
fontsize=font_size,
)
plt.show()
最終結(jié)果
你可以看到鋼管被正確識(shí)別,并且每個(gè)鋼管都有一個(gè)關(guān)聯(lián)的 ID。你可以修改代碼并嘗試許多其他用例。
你可以按照上述步驟在自己的 GPU 上運(yùn)行這個(gè)模型。只要確保有足夠的 RAM 以及較小的圖像輸入尺寸(本文示例使用 16GB 的 RAM,圖像只有幾百 KB)。
Article link:https://medium.com/@alejandro7899871776/point-and-count-objects-using-small-vlms-on-your-local-machine-3a769c7f2b6c