Heim  >  Artikel  >  Technologie-Peripheriegeräte  >  Erstellen einer Videosuchmaschine mit CLIP

Erstellen einer Videosuchmaschine mit CLIP

WBOY
WBOYnach vorne
2023-04-12 13:43:03836Durchsuche

CLIP (Contrastive Language-Image Pre-training) ist eine maschinelle Lerntechnologie, die Bilder und Texte in natürlicher Sprache genau verstehen und klassifizieren kann, was einen tiefgreifenden Einfluss auf die Bild- und Sprachverarbeitung hat und als beliebtes Diffusionsmodell verwendet wird zugrunde liegender Mechanismus von DALL-E. In diesem Beitrag erfahren Sie, wie Sie CLIP zur Unterstützung der Videosuche anpassen.

Dieser Artikel geht nicht auf die technischen Details des CLIP-Modells ein, sondern zeigt eine weitere praktische Anwendung von CLIP (zusätzlich zum Diffusionsmodell).

Zuerst müssen wir wissen: CLIP verwendet einen Bilddecoder und einen Textencoder, um vorherzusagen, welche Bilder im Datensatz mit welchem ​​Text übereinstimmen.

Erstellen einer Videosuchmaschine mit CLIP

Suche mit CLIP

Durch die Verwendung des vorab trainierten CLIP-Modells von Hugging Face können wir ein einfaches, aber leistungsstarkes Modell erstellen Es handelt sich um eine Videosuchmaschine mit natürlichen Sprachfunktionen, für die kein Feature-Engineering erforderlich ist.

Wir müssen die folgende Software verwenden

Python≥= 3.8,ffmpeg,opencv

Es gibt viele Techniken zum Durchsuchen von Videos anhand von Text. Wir können uns eine Suchmaschine so vorstellen, dass sie aus zwei Teilen besteht: Indizierung und Suche.

INDEX

Die Videoindizierung umfasst typischerweise eine Kombination aus manuellen und maschinellen Prozessen. Menschen verarbeiten Videos vor, indem sie relevante Schlüsselwörter in Titeln, Tags und Beschreibungen hinzufügen, während automatisierte Prozesse visuelle und akustische Funktionen wie Objekterkennung und Audiotranskription extrahieren. Benutzerinteraktionsmetriken und mehr, die aufzeichnen, welche Teile des Videos am relevantesten sind und wie lange sie relevant bleiben. Alle diese Schritte helfen dabei, einen durchsuchbaren Index Ihrer Videoinhalte zu erstellen.

Eine Übersicht über den Indexierungsprozess ist wie folgt

  • Teilen Sie das Video in mehrere Szenen auf
  • Sampling-Szenen für den Frame #🎜 ?? in mehrere Szenen# 🎜🎜#
  • Warum ist die Szenenerkennung wichtig? Videos bestehen aus Szenen und Szenen bestehen aus ähnlichen Bildern. Wenn wir nur beliebige Szenen im Video abtasten, entgehen uns möglicherweise Keyframes im gesamten Video.
  • Wir müssen also bestimmte Ereignisse oder Aktionen im Video genau identifizieren und lokalisieren. Wenn ich beispielsweise nach „Hunde im Park“ suche und das gesuchte Video mehrere Szenen enthält, beispielsweise eine Szene mit einem Mann auf einem Fahrrad und eine Szene mit einem Hund im Park, ermöglicht mir die Szenenerkennung dies Identifizieren Sie diejenigen, die für die Suchabfrage am relevantesten sind.
  • Sie können das Python-Paket „Szenenerkennung“ verwenden, um diesen Vorgang auszuführen.
 mport scenedetect as sd
 
 video_path = '' # path to video on machine
 
 video = sd.open_video(video_path)
 sm = sd.SceneManager()
 
 sm.add_detector(sd.ContentDetector(threshold=27.0))
 sm.detect_scenes(video)
 
 scenes = sm.get_scene_list()

Sampling der Frames der SzeneErstellen einer Videosuchmaschine mit CLIP

Dann müssen Sie cv2 verwenden, um das Video zu rahmen.

 import cv2
 
 cap = cv2.VideoCapture(video_path)
 
 every_n = 2 # number of samples per scene
 
 scenes_frame_samples = []
 for scene_idx in range(len(scenes)):
scene_length = abs(scenes[scene_idx][0].frame_num - scenes[scene_idx][1].frame_num)
every_n = round(scene_length/no_of_samples)
local_samples = [(every_n * n) + scenes[scene_idx][0].frame_num for n in range(3)]
 
scenes_frame_samples.append(local_samples)

Frames in Pixeleinbettungen umwandeln

Nachdem wir die Proben gesammelt haben, müssen wir sie in etwas berechnen, das vom CLIP-Modell verwendet werden kann.

Zuerst müssen Sie jedes Beispiel in eine Bildtensor-Einbettung umwandeln.

 from transformers import CLIPProcessor
 from PIL import Image
 
 clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
 
 def clip_embeddings(image):
inputs = clip_processor(images=image, return_tensors="pt", padding=True)
input_tokens = {
k: v for k, v in inputs.items()
}
 
return input_tokens['pixel_values']
 
 # ...
 scene_clip_embeddings = [] # to hold the scene embeddings in the next step
 
 for scene_idx in range(len(scenes_frame_samples)):
scene_samples = scenes_frame_samples[scene_idx]
 
pixel_tensors = [] # holds all of the clip embeddings for each of the samples
for frame_sample in scene_samples:
cap.set(1, frame_sample)
ret, frame = cap.read()
if not ret:
print('failed to read', ret, frame_sample, scene_idx, frame)
break
 
pil_image = Image.fromarray(frame)
 
clip_pixel_values = clip_embeddings(pil_image)
pixel_tensors.append(clip_pixel_values)

Der nächste Schritt besteht darin, alle Samples in derselben Szene zu mitteln, was die Dimensionalität des Samples reduzieren und auch das Problem des Rauschens in einem einzelnen Sample lösen kann.

 import torch
 import uuid
 
 def save_tensor(t):
path = f'/tmp/{uuid.uuid4()}'
torch.save(t, path)
 
return path
 
 # ..
 avg_tensor = torch.mean(torch.stack(pixel_tensors), dim=0)
 scene_clip_embeddings.append(save_tensor(avg_tensor))

Auf diese Weise wird eine in CLIP eingebettete Tensorliste erhalten, die den Videoinhalt darstellt.

Speicherindex

Für den zugrunde liegenden Indexspeicher verwenden wir LevelDB (LevelDB ist eine von Google verwaltete Schlüssel-/Wertbibliothek). Die Architektur unserer Suchmaschine besteht aus drei separaten Indizes:

Videoszenenindex: Welche Szenen gehören zu einem bestimmten Video

Szeneneinbettungsindex: Spezifisch speichern Szenen Daten

Video-Metadaten-Index: Speichern Sie die Metadaten des Videos.

Wir fügen zunächst alle berechneten Metadaten im Video und die eindeutige Kennung des Videos in den Metadatenindex ein. Dieser Schritt ist bereits fertig und sehr einfach.

 import leveldb
 import uuid
 
 def insert_video_metadata(videoID, data):
b = json.dumps(data)
 
level_instance = leveldb.LevelDB('./dbs/videometadata_index')
level_instance.Put(videoID.encode('utf-8'), b.encode('utf-8'))
 
 # ...
 video_id = str(uuid.uuid4())
 insert_video_metadata(video_id, {
'VideoURI': video_path,
 })

Erstellen Sie dann einen neuen Eintrag im Szeneneinbettungsindex, um jedes im Video eingebettete Pixel zu speichern, und benötigen Sie außerdem eine eindeutige Kennung, um jede Szene zu identifizieren.
    import leveldb
     import uuid
     
     def insert_scene_embeddings(sceneID, data):
    level_instance = leveldb.LevelDB('./dbs/scene_embedding_index')
    level_instance.Put(sceneID.encode('utf-8'), data)
     
     # ...
     for f in scene_clip_embeddings:
    scene_id = str(uuid.uuid4())
     
    with open(f, mode='rb') as file:
    content = file.read()
     
    insert_scene_embeddings(scene_id, content)
  • Zuletzt müssen wir noch speichern, welche Szenen zu welchem ​​Video gehören.
  •  import leveldb
     import uuid
     
     def insert_video_scene(videoID, sceneIds):
    b = ",".join(sceneIds)
     
    level_instance = leveldb.LevelDB('./dbs/scene_index')
    level_instance.Put(videoID.encode('utf-8'), b.encode('utf-8'))
     
     # ...
     scene_ids = []
     for f in scene_clip_embeddings:
    # .. as shown in previous step
    scene_ids.append(scene_id)
    scene_embedding_index.insert(scene_id, content)
     
     scene_index.insert(video_id, scene_ids)
  • Suchen
  • Da wir nun einen Index von Videos haben, können wir sie basierend auf der Modellausgabe suchen und sortieren.
Der erste Schritt erfordert das Durchlaufen aller Datensätze im Szenenindex. Erstellen Sie dann eine Liste aller Videos und übereinstimmender Szenen-IDs im Video.

 records = []
 
 level_instance = leveldb.LevelDB('./dbs/scene_index')
 
 for k, v in level_instance.RangeIter():
record = (k.decode('utf-8'), str(v.decode('utf-8')).split(','))
records.append(record)

Der nächste Schritt erfordert das Sammeln aller in jedem Video vorhandenen Szeneneinbettungstensoren.

import leveldb

def get_tensor_by_scene_id(id):
level_instance = leveldb.LevelDB('./dbs/scene_embedding_index')
b = level_instance.Get(bytes(id,'utf-8'))

return BytesIO(b)

for r in records:
tensors = [get_tensor_by_scene_id(id) for id in r[1]]

Nachdem wir alle Tensoren haben, aus denen das Video besteht, können wir es an das Modell übergeben. Die Eingabe in das Modell ist „pixel_values“, ein Tensor, der die Videoszene darstellt.

import torch
from transformers import CLIPProcessor, CLIPModel

processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")

inputs = processor(text=text, return_tensors="pt", padding=True)

for tensor in tensors:
image_tensor = torch.load(tensor)
inputs['pixel_values'] = image_tensor 
outputs = model(**inputs)

Greifen Sie dann in der Modellausgabe auf „logits_per_image“ zu, um die Ausgabe des Modells zu erhalten.

Logits sind im Wesentlichen die rohen, nicht normalisierten Vorhersagen des Netzwerks. Da wir nur eine Textzeichenfolge und einen Tensor bereitstellen, der die Szene im Video darstellt, ist die Struktur des Logits eine einwertige Vorhersage.

logits_per_image = outputs.logits_per_image
probs = logits_per_image.squeeze()

prob_for_tensor = probs.item()

Addieren Sie die Wahrscheinlichkeiten für jede Iteration und dividieren Sie sie durch die Gesamtzahl der Tensoren am Ende der Operation, um die durchschnittliche Wahrscheinlichkeit des Videos zu erhalten.

def clip_scenes_avg(tensors, text):
avg_sum = 0.0

for tensor in tensors:
# ... previous code snippets
probs = probs.item()
avg_sum += probs.item()
 
return avg_sum / len(tensors)

最后在得到每个视频的概率并对概率进行排序后,返回请求的搜索结果数目。

import leveldb
import json

top_n = 1 # number of search results we want back

def video_metadata_by_id(id):
level_instance = leveldb.LevelDB('./dbs/videometadata_index')
b = level_instance.Get(bytes(id,'utf-8'))
return json.loads(b.decode('utf-8'))

results = []
for r in records:
# .. collect scene tensors

# r[0]: video id
return (clip_scenes_avg, r[0]) 

sorted = list(results)
sorted.sort(key=lambda x: x[0], reverse=True)

results = []
for s in sorted[:top_n]:
data = video_metadata_by_id(s[1])

results.append({
'video_id': s[1],
'score': s[0],
'video_uri': data['VideoURI']
 })

就是这样!现在就可以输入一些视频并测试搜索结果。

总结

通过CLIP可以轻松地创建一个频搜索引擎。使用预训练的CLIP模型和谷歌的LevelDB,我们可以对视频进行索引和处理,并使用自然语言输入进行搜索。通过这个搜索引擎使用户可以轻松地找到相关的视频,最主要的是我们并不需要大量的预处理或特征工程。

那么我们还能有什么改进呢?

  • 使用场景的时间戳来确定最佳场景。
  • 修改预测让他在计算集群上运行。
  • 使用向量搜索引擎,例如Milvus 替代LevelDB
  • 在索引的基础上建立推荐系统
  • 等等
最后:

可以在这里找到本文的代码:https://github.com/GuyARoss/CLIP-video-search/tree/article-01。

以及这个修改版本:https://github.com/GuyARoss/CLIP-video-search。

Das obige ist der detaillierte Inhalt vonErstellen einer Videosuchmaschine mit CLIP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:51cto.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen