Transformers documentation
Video sınıflandırma
Video sınıflandırma
Video sınıflandırma, bir videoya etiket veya sınıf atama görevidir. Videoların her biri için yalnızca bir sınıfa sahip olması beklenir. Video sınıflandırma modelleri bir videoyu girdi olarak alır ve videonun hangi sınıfa ait olduğuna dair bir tahmin döndürür. Bu modeller, bir videonun içeriğini kategorize etmek için kullanılabilir. Video sınıflandırmanın gerçek dünyadaki bir uygulaması, fitness uygulamaları için faydalı olan eylem/etkinlik tanımadır. Ayrıca, özellikle toplu taşıma kullanırken görme engelli bireyler için de faydalıdır.
Bu kılavuz size şunları gösterecektir:
- VideoMAE modelini UCF101 veri setinin bir alt kümesi üzerinde ince ayar yapın.
- İnce ayarladığınız modeli çıkarım için kullanın.
Bu görevle uyumlu tüm mimarileri ve denetim noktalarını görmek için görev sayfasını kontrol etmenizi öneririz.
Başlamadan önce, gerekli tüm kütüphanelerin yüklendiğinden emin olun:
pip install -q pytorchvideo transformers evaluate
Videoları işlemek ve hazırlamak için PyTorchVideo (adlandırılmış pytorchvideo) kullanacaksınız.
Modelinizi yükleyip toplulukla paylaşabilmeniz için Hugging Face hesabınıza giriş yapmanızı öneririz. İstendiğinde giriş yapmak için token'ınızı girin:
>>> from huggingface_hub import notebook_login
>>> notebook_login()UCF101 veri setini yükle
Önce UCF-101 veri setinin bir alt kümesini yükleyerek başlayın. Bu, tam veri setinde eğitim için daha fazla zaman harcamadan önce her şeyin çalıştığını doğrulamanız ve deneyler yapmanız için size bir fırsat verecektir.
>>> from huggingface_hub import hf_hub_download
>>> hf_dataset_identifier = "sayakpaul/ucf101-subset"
>>> filename = "UCF101_subset.tar.gz"
>>> file_path = hf_hub_download(repo_id=hf_dataset_identifier, filename=filename, repo_type="dataset")Alt küme indirildikten sonra, sıkıştırılmış arşivi çıkarmalısınız:
>>> import tarfile
>>> with tarfile.open(file_path) as t:
... t.extractall(".")Genel hatlarıyla veri setinin organizasyonu şu şekildedir:
UCF101_subset/
train/
BandMarching/
video_1.mp4
video_2.mp4
...
Archery
video_1.mp4
video_2.mp4
...
...
val/
BandMarching/
video_1.mp4
video_2.mp4
...
Archery
video_1.mp4
video_2.mp4
...
...
test/
BandMarching/
video_1.mp4
video_2.mp4
...
Archery
video_1.mp4
video_2.mp4
...
...Ardından toplam video sayısını sayabilirsiniz.
>>> import pathlib
>>> dataset_root_path = "UCF101_subset"
>>> dataset_root_path = pathlib.Path(dataset_root_path)>>> video_count_train = len(list(dataset_root_path.glob("train/*/*.avi")))
>>> video_count_val = len(list(dataset_root_path.glob("val/*/*.avi")))
>>> video_count_test = len(list(dataset_root_path.glob("test/*/*.avi")))
>>> video_total = video_count_train + video_count_val + video_count_test
>>> print(f"Total videos: {video_total}")>>> all_video_file_paths = (
... list(dataset_root_path.glob("train/*/*.avi"))
... + list(dataset_root_path.glob("val/*/*.avi"))
... + list(dataset_root_path.glob("test/*/*.avi"))
... )
>>> all_video_file_paths[:5](sorted) video yolları şu şekilde görünür:
...
'UCF101_subset/train/ApplyEyeMakeup/v_ApplyEyeMakeup_g07_c04.avi',
'UCF101_subset/train/ApplyEyeMakeup/v_ApplyEyeMakeup_g07_c06.avi',
'UCF101_subset/train/ApplyEyeMakeup/v_ApplyEyeMakeup_g08_c01.avi',
'UCF101_subset/train/ApplyEyeMakeup/v_ApplyEyeMakeup_g09_c02.avi',
'UCF101_subset/train/ApplyEyeMakeup/v_ApplyEyeMakeup_g09_c06.avi'
...Video dosya yollarında grup g ile belirtilen, aynı grup / sahneye ait video klipler olduğunu fark edeceksiniz. Örneğin v_ApplyEyeMakeup_g07_c04.avi ve v_ApplyEyeMakeup_g07_c06.avi.
Doğrulama ve değerlendirme bölümleri için, veri sızıntısını önlemek amacıyla aynı grup/sahne ait video kliplerinin bulunmasını istemezsiniz. Bu öğreticide kullandığınız alt küme bu bilgiyi dikkate alır.
Sıradaki adımda, veri setinde bulunan etiket kümesini türeteceksiniz. Ayrıca, modeli başlatırken işinize yarayacak iki sözlük oluşturun:
label2id: sınıf adlarını tamsayılara eşler.id2label: tamsayıları sınıf adlarına eşler.
>>> class_labels = sorted({str(path).split("/")[2] for path in all_video_file_paths})
>>> label2id = {label: i for i, label in enumerate(class_labels)}
>>> id2label = {i: label for label, i in label2id.items()}
>>> print(f"Unique classes: {list(label2id.keys())}.")
# Unique classes: ['ApplyEyeMakeup', 'ApplyLipstick', 'Archery', 'BabyCrawling', 'BalanceBeam', 'BandMarching', 'BaseballPitch', 'Basketball', 'BasketballDunk', 'BenchPress'].10 benzersiz sınıf bulunmaktadır. Her sınıf için eğitim setinde 30 video vardır.
İnce ayar yapmak için bir model yükleyin
Önceden eğitilmiş bir kontrol noktasından ve ilişkili görüntü işleyicisinden bir video sınıflandırma modeli örneği oluşturun. Modelin kodlayıcısı önceden eğitilmiş parametrelerle birlikte gelir ve sınıflandırma başlığı rastgele başlatılır. Görüntü işleyici, veri setimiz için ön işleme hattını yazarken işimize yarayacaktır.
>>> from transformers import VideoMAEImageProcessor, VideoMAEForVideoClassification
>>> model_ckpt = "MCG-NJU/videomae-base"
>>> image_processor = VideoMAEImageProcessor.from_pretrained(model_ckpt)
>>> model = VideoMAEForVideoClassification.from_pretrained(
... model_ckpt,
... label2id=label2id,
... id2label=id2label,
... ignore_mismatched_sizes=True, # provide this in case you're planning to fine-tune an already fine-tuned checkpoint
... )Model yüklenirken aşağıdaki uyarıyı görebilirsiniz:
Some weights of the model checkpoint at MCG-NJU/videomae-base were not used when initializing VideoMAEForVideoClassification: [..., 'decoder.decoder_layers.1.attention.output.dense.bias', 'decoder.decoder_layers.2.attention.attention.key.weight']
- This IS expected if you are initializing VideoMAEForVideoClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing VideoMAEForVideoClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Some weights of VideoMAEForVideoClassification were not initialized from the model checkpoint at MCG-NJU/videomae-base and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.Uyarı, bazı ağırlıkları (örneğin classifier katmanının ağırlıkları ve sapması) atıp bazı diğerlerini (yeni classifier katmanının ağırlıkları ve sapması) rastgele başlattığımızı söylüyor. Bu durumda bu beklenen bir durumdur, çünkü önceden eğitilmiş ağırlıkları bulunmayan yeni bir başlık ekliyoruz; bu nedenle kütüphane, bu modeli çıkarım için kullanmadan önce ince ayar yapmamız gerektiği konusunda bizi uyarıyor ve biz de tam olarak bunu yapacağız.
Not: bu checkpoint, önemli ölçüde alan örtüşmesine sahip benzer bir aşağı akış görevinde ince ayar yapılarak elde edildiğinden, bu görevde daha iyi performans sağlar. MCG-NJU/videomae-base-finetuned-kinetics modelinin ince ayarıyla elde edilen bu checkpoint'e göz atabilirsiniz.
Eğitim için veri setlerini hazırlayın
Videoları ön işlemek için PyTorchVideo kütüphanesini kullanacaksınız. Başlamak için gerekli bağımlılıkları içe aktarın.
>>> import pytorchvideo.data
>>> from pytorchvideo.transforms import (
... ApplyTransformToKey,
... Normalize,
... RandomShortSideScale,
... RemoveKey,
... ShortSideScale,
... UniformTemporalSubsample,
... )
>>> from torchvision.transforms import (
... Compose,
... Lambda,
... RandomCrop,
... RandomHorizontalFlip,
... Resize,
... )Eğitim veri seti dönüşümleri için, uniform temporal subsampling, piksel normalizasyonu, rastgele kırpma ve rastgele yatay çevirme kombinasyonunu kullanın. Doğrulama ve değerlendirme veri seti dönüşümleri için, rastgele kırpma ve yatay çevirme hariç aynı dönüşüm zincirini koruyun. Bu dönüşümlerin ayrıntıları hakkında daha fazla bilgi edinmek için PyTorchVideo resmi dokümantasyonuna göz atın.
Önceden eğitilmiş modelle ilişkili image_processor öğesini kullanarak aşağıdaki bilgileri alın:
- Video kare piksellerinin normalize edileceği görüntü ortalaması ve standart sapması.
- Video karelerinin yeniden boyutlandırılacağı uzamsal çözünürlük.
Bazı sabitleri tanımlayarak başlayın.
>>> mean = image_processor.image_mean
>>> std = image_processor.image_std
>>> if "shortest_edge" in image_processor.size:
... height = width = image_processor.size["shortest_edge"]
>>> else:
... height = image_processor.size["height"]
... width = image_processor.size["width"]
>>> resize_to = (height, width)
>>> num_frames_to_sample = model.config.num_frames
>>> sample_rate = 4
>>> fps = 30
>>> clip_duration = num_frames_to_sample * sample_rate / fpsŞimdi, veri setine özgü dönüşümleri ve veri setlerini sırasıyla tanımlayın. Eğitim setiyle başlayalım:
>>> train_transform = Compose(
... [
... ApplyTransformToKey(
... key="video",
... transform=Compose(
... [
... UniformTemporalSubsample(num_frames_to_sample),
... Lambda(lambda x: x / 255.0),
... Normalize(mean, std),
... RandomShortSideScale(min_size=256, max_size=320),
... RandomCrop(resize_to),
... RandomHorizontalFlip(p=0.5),
... ]
... ),
... ),
... ]
... )
>>> train_dataset = pytorchvideo.data.Ucf101(
... data_path=os.path.join(dataset_root_path, "train"),
... clip_sampler=pytorchvideo.data.make_clip_sampler("random", clip_duration),
... decode_audio=False,
... transform=train_transform,
... )Aynı iş akışı dizisi, doğrulama ve değerlendirme kümelerine de uygulanabilir:
>>> val_transform = Compose(
... [
... ApplyTransformToKey(
... key="video",
... transform=Compose(
... [
... UniformTemporalSubsample(num_frames_to_sample),
... Lambda(lambda x: x / 255.0),
... Normalize(mean, std),
... Resize(resize_to),
... ]
... ),
... ),
... ]
... )
>>> val_dataset = pytorchvideo.data.Ucf101(
... data_path=os.path.join(dataset_root_path, "val"),
... clip_sampler=pytorchvideo.data.make_clip_sampler("uniform", clip_duration),
... decode_audio=False,
... transform=val_transform,
... )
>>> test_dataset = pytorchvideo.data.Ucf101(
... data_path=os.path.join(dataset_root_path, "test"),
... clip_sampler=pytorchvideo.data.make_clip_sampler("uniform", clip_duration),
... decode_audio=False,
... transform=val_transform,
... )Not: Yukarıdaki veri seti boru hatları resmî PyTorchVideo örneğinden alınmıştır. UCF-101 veri setine özel olarak tasarlandığı için pytorchvideo.data.Ucf101() fonksiyonunu kullanıyoruz. Arka planda bir pytorchvideo.data.labeled_video_dataset.LabeledVideoDataset nesnesi döndürür. LabeledVideoDataset sınıfı, PyTorchVideo veri setindeki tüm video işlemleri için temel sınıftır. Dolayısıyla, PyTorchVideo tarafından hazır olarak desteklenmeyen özel bir veri seti kullanmak istiyorsanız, LabeledVideoDataset sınıfını buna göre genişletebilirsiniz. Daha fazla bilgi edinmek için data API belgelerini inceleyin. Ayrıca, veri setiniz yukarıda gösterilen yapıya benzer bir yapıya sahipse, pytorchvideo.data.Ucf101() kullanımı sorunsuz bir şekilde çalışacaktır.
Veri setindeki video sayısını öğrenmek için num_videos argümanına erişebilirsiniz.
>>> print(train_dataset.num_videos, val_dataset.num_videos, test_dataset.num_videos)
# (300, 30, 75)Hata ayıklamayı iyileştirmek için ön işlenmiş videoyu görselleştirin
>>> import imageio
>>> import numpy as np
>>> from IPython.display import Image
>>> def unnormalize_img(img):
... """Un-normalizes the image pixels."""
... img = (img * std) + mean
... img = (img * 255).astype("uint8")
... return img.clip(0, 255)
>>> def create_gif(video_tensor, filename="sample.gif"):
... """Prepares a GIF from a video tensor.
...
... The video tensor is expected to have the following shape:
... (num_frames, num_channels, height, width).
... """
... frames = []
... for video_frame in video_tensor:
... frame_unnormalized = unnormalize_img(video_frame.permute(1, 2, 0).numpy())
... frames.append(frame_unnormalized)
... kargs = {"duration": 0.25}
... imageio.mimsave(filename, frames, "GIF", **kargs)
... return filename
>>> def display_gif(video_tensor, gif_name="sample.gif"):
... """Prepares and displays a GIF from a video tensor."""
... video_tensor = video_tensor.permute(1, 0, 2, 3)
... gif_filename = create_gif(video_tensor, gif_name)
... return Image(filename=gif_filename)
>>> sample_video = next(iter(train_dataset))
>>> video_tensor = sample_video["video"]
>>> display_gif(video_tensor)
Modeli eğitin
Modeli eğitmek için 🤗 Transformers'tan Trainer'ı kullanın. Bir Trainer örneği oluşturmak için eğitim yapılandırmasını ve bir değerlendirme metriğini tanımlamanız gerekir. En önemli bileşen, eğitimi yapılandırmak için gereken tüm öznitelikleri içeren TrainingArguments sınıfıdır. Bu sınıf, modelin denetim noktalarını (checkpoint) kaydetmek için kullanılacak bir çıktı klasörü adı gerektirir. Ayrıca 🤗 Hub'daki model deposundaki tüm bilgilerin senkronize edilmesine yardımcı olur.
Eğitim argümanlarının çoğu kendi kendini açıklar nitelikte olmakla birlikte, burada oldukça önemli olan bir argüman remove_unused_columns=False'dir. Bu argüman, modelin call fonksiyonu tarafından kullanılmayan tüm özellikleri (features) düşürür. Varsayılan olarak True'dir çünkü genellikle kullanılmayan özellik sütunlarını düşürmek idealdir; bu da girdilerin modelin call fonksiyonuna daha kolay açılmasını sağlar. Ancak bu durumda, modelin girdilerinde beklediği zorunlu bir anahtar olan pixel_values'i oluşturabilmek için kullanılmayan özelliklere (özellikle 'video'ya) ihtiyacınız vardır.
>>> from transformers import TrainingArguments, Trainer
>>> model_name = model_ckpt.split("/")[-1]
>>> new_model_name = f"{model_name}-finetuned-ucf101-subset"
>>> num_epochs = 4
>>> args = TrainingArguments(
... new_model_name,
... remove_unused_columns=False,
... eval_strategy="epoch",
... save_strategy="epoch",
... learning_rate=5e-5,
... per_device_train_batch_size=batch_size,
... per_device_eval_batch_size=batch_size,
... warmup_steps=0.1,
... logging_steps=10,
... load_best_model_at_end=True,
... metric_for_best_model="accuracy",
... push_to_hub=True,
... max_steps=(train_dataset.num_videos // batch_size) * num_epochs,
... )pytorchvideo.data.Ucf101() tarafından döndürülen veri kümesi __len__ yöntemini uygulamaz. Bu nedenle, TrainingArguments örneğini oluştururken max_steps parametresini tanımlamamız gerekir.
Ardından, şimdi yükleyeceğiniz metric değerini kullanacak, tahminlerden metrikleri hesaplamak için bir fonksiyon tanımlamanız gerekir. Yapmanız gereken tek ön işleme, tahmin edilen logitlerin argmax değerini almaktır:
import evaluate
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
predictions = np.argmax(eval_pred.predictions, axis=1)
return metric.compute(predictions=predictions, references=eval_pred.label_ids)Değerlendirme hakkında bir not:
VideoMAE makalesinde yazarlar aşağıdaki değerlendirme stratejisini kullanır. Modeli test videolarından alınan birkaç klipte değerlendirir ve bu kliplere farklı kırpma uygulamalarının ardından toplam skoru raporlar. Ancak basitlik ve özlülük amacıyla bu öğreticide bu durumu göz önünde bulundurmuyoruz.
Ayrıca, örnekleri toplu (batch) hâle getirmek için kullanılacak bir collate_fn tanımlayın. Her batch, pixel_values ve labels olmak üzere 2 anahtardan oluşur.
>>> def collate_fn(examples):
... # permute to (num_frames, num_channels, height, width)
... pixel_values = torch.stack(
... [example["video"].permute(1, 0, 2, 3) for example in examples]
... )
... labels = torch.tensor([example["label"] for example in examples])
... return {"pixel_values": pixel_values, "labels": labels}Sonra tüm bunları veri setleriyle birlikte Trainer'a iletmeniz yeterlidir:
>>> trainer = Trainer(
... model,
... args,
... train_dataset=train_dataset,
... eval_dataset=val_dataset,
... processing_class=image_processor,
... compute_metrics=compute_metrics,
... data_collator=collate_fn,
... )Verileri zaten ön işlediğiniz halde neden image_processor'u bir tokenizer olarak ilettiğinizi merak ediyor olabilirsiniz. Bu, görüntü işleyici yapılandırma dosyasının (JSON olarak saklanır) Hub'daki repoya da yükleneceğinden emin olmak içindir.
Şimdi train metodunu çağırarak modelimizi ince ayarlayalım:
>>> train_results = trainer.train()Eğitim tamamlandıktan sonra, modelinizi herkesin kullanabilmesi için push_to_hub() yöntemiyle Hub'a paylaşın:
>>> trainer.push_to_hub()Çıkarım
Harika, bir modeli ince ayarladığınıza göre artık çıkarım için kullanabilirsiniz!
Çıkarım için bir video yükleyin:
>>> sample_test_video = next(iter(test_dataset))
İnce ayarını yaptığınız modeli çıkarım için denemenin en basit yolu, onu bir VideoClassificationPipeline içinde kullanmaktır. Modelinizle video sınıflandırma için bir pipeline örneği oluşturun ve videonuzu ona iletin:
>>> from transformers import pipeline
from accelerate import Accelerator
>>> video_cls = pipeline(model="my_awesome_video_cls_model")
>>> video_cls("https://huggingface.co/datasets/sayakpaul/ucf101-subset/resolve/main/v_BasketballDunk_g14_c06.avi")
[{'score': 0.9272987842559814, 'label': 'BasketballDunk'},
{'score': 0.017777055501937866, 'label': 'BabyCrawling'},
{'score': 0.01663011871278286, 'label': 'BalanceBeam'},
{'score': 0.009560945443809032, 'label': 'BandMarching'},
{'score': 0.0068979403004050255, 'label': 'BaseballPitch'}]İsterseniz pipeline sonuçlarını manuel olarak da yeniden üretebilirsiniz.
>>> def run_inference(model, video):
... # (num_frames, num_channels, height, width)
... permuted_sample_test_video = video.permute(1, 0, 2, 3)
... inputs = {
... "pixel_values": perumuted_sample_test_video.unsqueeze(0),
... "labels": torch.tensor(
... [sample_test_video["label"]]
... ), # this can be skipped if you don't have labels available.
... }
... device = Accelerator().device
... inputs = {k: v.to(device) for k, v in inputs.items()}
... model = model.to(device)
... # forward pass
... with torch.no_grad():
... outputs = model(**inputs)
... logits = outputs.logits
... return logitsŞimdi, girdinizi modele iletin ve logits değerlerini döndürün:
>>> logits = run_inference(trained_model, sample_test_video["video"])logits değerlerini çözersek, şunu elde ederiz:
>>> predicted_class_idx = logits.argmax(-1).item()
>>> print("Predicted class:", model.config.id2label[predicted_class_idx])
# Predicted class: BasketballDunk