تنسورفلو ۱۳: دسته بندی ویدئو

سلام

بعد از چند ماه یه آموزش جدید به سری تنسورفلو اضافه می کنم :-)

در این آموزش می خواهیم ویدئو های دیتاست IXMAS Actions رو دسته بندی کنیم.

این دیتاست یکی از دیتاست های مورد استفاده در زمینه HAR یا تشخیص فعالیت های انسانی هست

نمونه ای از داده های IXMAS
نمونه ای از داده های IXMAS

در این دیتاست تعدادی ویدئو از چند فرد با چند دوربین در جهت های مختلف ضبط شده که افراد ۱۱ تا فعالیت مختلف رو انجام می دن.


قدم اول

دیتاست رو از اینجا دانلود کنید و محیط توسعه رو فراهم کنید :)

قدم دوم

در این قدم قصد داریم ویدئو رو به یک سری فریم تبدیل کنیم برای اینکار از openCV استفاده می کنیم

برای نصب openCV از خط زیر استفاده کنید:

pip install opencv-python


یک فایل پایتون به اسم vid-img-convertor.py بسازید و تکه کد زیر رو داخلش قرار بدید این کد کمک می‌کنه تا چند فریم از هر کدوم از clip ها استخراج کنیم:

import cv2
from os import listdir
folder_name = &quotnixmas&quot


def getFrame(sec, vid_name):
    vidcap.set(cv2.CAP_PROP_POS_MSEC, sec*1000)
    hasFrames, image = vidcap.read()
    if hasFrames:
        # save frame as JPG file
        cv2.imwrite(folder_name+&quot-img/&quot+vid_name+&quot_&quot+str(count)+&quot.jpg&quot, image)
    return hasFrames


for item in listdir(folder_name):
    print(item)
    if item.endswith('.avi'):
        vidcap = cv2.VideoCapture(folder_name+'/' + item)
        vid_name = item.replace('.avi', '')
        sec = 0
        frameRate = 0.5  # //it will capture image in each 0.5 second
        count = 1
        success = getFrame(sec, vid_name)
        while success:
            count = count + 1
            sec = sec + frameRate
            sec = round(sec, 2)
            success = getFrame(sec, vid_name)
        vidcap.release()

قدم سوم

از الان می‌تونیم شروع کنیم که با تنسورفلو کار کنیم. ابتدا کتاب‌خانه های لازم رو وارد می ‌کنیم

import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.models import Model

from sklearn.preprocessing import LabelEncoder

import matplotlib.pyplot as plt

from PIL import Image
import os
import numpy as np

colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

یک تابع تعریف می کنیم که تغییرات loss function رو چاپ کنیم

def plot_loss(history, label, n):
  # Use a log scale to show the wide range of values.
  plt.semilogy(history.epoch,  history.history['loss'],  color=colors[n], label='Train '+label)
  plt.semilogy(history.epoch,  history.history['val_loss'], color=colors[n], label='Val '+label, 
                       linestyle=&quot--&quot)
  plt.xlabel('Epoch')
  plt.ylabel('Loss')
  
  plt.legend()

بعد دیتا ها رو load می کنیم

data_x = []
data_y = []
for path in os.listdir('./nixmas-img'):
    img = Image.open('nixmas-img/'+path)
    data_x.append(np.array(img))
    data_y.append(path.split('_')[3])
print({i:data_y.count(i) for i in data_y})
le = LabelEncoder()
a = le.fit_transform(data_y)
b = np.zeros((a.size, a.max()+1))
b[np.arange(a.size),a] = 1
data_y = a

فرمت دیتا ها رو اطلاح می کنیم و همچنین دیتا ها رو تقسیم بر 255 می کنیم چون هر پیکسل مقداری بین 0 تا 255 داره و می خواهیم مقادیر بین 0 تا 1 قرار بگیره

data_x = np.array(data_x)
data_x = data_x.astype(np.float32)
data_x /= 255.0

دیتا ها رو به فرمت دیتاست تنسورفلو تبدیل می کنیم

dataset = tf.data.Dataset.from_tensor_slices((data_x, data_y))
dataset = dataset.cache().shuffle(10000)

دیتاستمون رو به چند قسمت برای test, validate و train تقسیم می‌کنیم

test_dataset = dataset.take(30).batch(10)
val_dataset = dataset.skip(30).take(100).batch(10)
train_dataset = dataset.skip(130).batch(100)

حالا که دیتا آماده است سعی می کنیم مدل رو بسازیم. برای این تسک از الگوریتم CNN استفاده می‌کنیم توی این پست در مورد CNN صحبت کردم

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(64, 48, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(11))

الان دیگه می‌تونیم مدل رو compile کنیم و نتیجه ساختار مدل رو مشاهده کنیم

model.compile(optimizer='adam', 
               loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),       
               metrics=['accuracy'])
model.summary()


Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 62, 46, 32) 896
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 31, 23, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 29, 21, 64) 18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 10, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 12, 8, 64) 36928
_________________________________________________________________
flatten (Flatten) (None, 6144) 0
_________________________________________________________________
dense (Dense) (None, 64) 393280
_________________________________________________________________
dense_1 (Dense) (None, 11) 715
=================================================================
Total params: 450,315
Trainable params: 450,315
Non-trainable params: 0

خب بالاخره می‌تونیم مدل رو train کنیم

history = model.fit(train_dataset, validation_data=val_dataset, epochs=60)

بیایید نمودار بکشیم :-)

plot_loss(history, &quothistory&quot, 1)
نمودار loss function
نمودار loss function

ببینید نمودار نزولی هست و همزمان ‌‌loss دیتاست validate و train در حال کاهش هست. نکته مهم اینه که نمودار val تقریبا همزمان با train در حال کاهش هست و معنی این رو میده که مدل overfit نشده اما نوسانات آخر می‌تونه معنی بده شاید چون دیتا کمه. شاید اگه از الگوریتم دیگه مثل RNN استفاده کنیم چونکه ورودی ما ویدئو هست نتیجه بهتر هم بشه


در آخر هم بر روی دیتاست تست خروجی رو تست می کنیم

model.evaluate(test_dataset)


خروجی که من از این مدل گرفتم به صورت زیر است:

train_loss: 0.0541 - train_accuracy: 0.9819

val_loss: 0.0097 - val_accuracy: 1.0000

test_loss: 0.0829 - test_accuracy: 0.9667