VİDEO SINIFLANDIRMA

Original article was published by sevim sevilgen on Artificial Intelligence on Medium


Photo by Noom Peerapong on Unsplash

Merhabalar bu blog yazısında colab ortamında resimlerden oluşan verisetini kullanarak taş, kağıt ve makas oyununu görüntülerle eğitip, videodan test ederek video da hangi hareketi yaptığını tespit edeceğiz.

  • İlk önce gerekli dosyaları driver hesabınıza aktarıyorsunuz. Verisetine ulaşmak için kaggle kaynağına bakabilirsiniz.
  • Daha sonra driver ‘ınızda aşağıdaki gibi bir colab sayfası açacaksınız.
  • Colab ‘ı driver ‘a bağlayıp oradaki kaynakları kullanmak için colab kod satırında aşağıdaki kodu çalıştırıp verdiği link bağlantısına gidip şifreyi almalısınız. Şifreyi kodu çalıştırdıktan sonra altına gelen kutucuğa yapıştıracaksınız.
from google.colab import drivedrive.mount('/content/drive/')

Eğitim yapmak için ilk önce train.py dosyasını oluşturacağız.

  • Arka ucu ayarlar, böylece eğitim grafiğimizi bir resim dosyasına verebiliriz.
import matplotlibmatplotlib.use("Agg")

Gerekli Kütüphaneler:

  • tensorflow.keras: Derin öğrenme için kullanılır. Modelimiz için bu kütüphaneyi kullanacağız.
  • sklearn: Scikit-learn’den, sınıf etiketlerimizi tek kodlama için bir LabelBinarizer uygulamasını kullanacağız.
  • Train_test_split: İşlevi, veri kümemizi eğitim ve test bölümlerine ayırır. Ayrıca, geleneksel bir biçimde bir sınıflandırma raporu yazdıracağız.
  • Path: Belirli bir yoldaki tüm görüntü dosyalarını listelemek için kullanışlı işlevdir. Oradan görsellerimizi belleğe yükleyebileceğiz.
  • Numpy: Sayısal işlemler için kullanılır.
  • argparse: Komut satırı argümanları, çalışma zamanında bir programa/komut dosyasına verilen bayraklardır. Programın çalıştırılabilmesi için ek bilgiler içerirler.
  • Pickle: Etiket ikilileştiricimizi diske seri hale getirmek için.
from tensorflow.keras.preprocessing.image import ImageDataGeneratorfrom tensorflow.keras.layers import AveragePooling2Dfrom tensorflow.keras.applications import ResNet101from tensorflow.keras.layers import Dropoutfrom tensorflow.keras.layers import Flattenfrom tensorflow.keras.layers import Densefrom tensorflow.keras.layers import Inputfrom tensorflow.keras.models import Modelfrom tensorflow.keras.optimizers import SGDfrom sklearn.preprocessing import LabelBinarizerfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_reportfrom imutils import pathsimport matplotlib.pyplot as pltimport numpy as npimport argparseimport pickleimport cv2import os

Argümanlar

  • dataset: Giriş veri kümesinin yolu.
  • model: Çıktı Keras model dosyamıza giden yolumuz.
  • label-bin: Çıktı etiketi ikili ayırıcı turşu dosyamızın yolu.
  • epochs: Ağımızı kaç dönem eğitmek için — varsayılan olarak 25 dönem için eğitim vereceğiz, ancak eğiticide daha sonra göstereceğim gibi, 50 dönem daha iyi sonuçlar sağlayabilir.
  • plot: Çıktı çizim resim dosyamızın yolu — varsayılan olarak plot.png olarak adlandırılacak ve bu eğitim komut dosyasıyla aynı dizine yerleştirilecektir.
ap = argparse.ArgumentParser()ap.add_argument("-d", "--dataset", required=True,help="path to input dataset")ap.add_argument("-m", "--model", required=True,help="path to output serialized model")ap.add_argument("-l", "--label-bin", required=True,help="path to output label binarizer")ap.add_argument("-e", "--epochs", type=int, default=25,help="# of epochs to train our network for")ap.add_argument("-p", "--plot", type=str, default="plot.png",help="path to output loss/accuracy plot")args = vars(ap.parse_args())
  • Sınıflarımın etiketleri bu kod satırında yazılır.
LABELS = set(["Paper", "Rock", "Scissors"])print("[INFO] loading images...")
  • Verilerimizin yolunu dataset argümanı ile alırız.
imagePaths = list(paths.list_images(args["dataset"]))
  • Veriler ve etiketler listelenir.
data = []labels = []
for imagePath in imagePaths:
  • Tüm veri kümesi imagePaths, args [“dataset”] (komut satırı argümanlarından gelen) içerdiği değer aracılığıyla toplanır.
label = imagePath.split(os.path.sep)[-2]
  • LABELS kümesinde olmayan herhangi bir etiketi yok sayar.
if label not in LABELS:continue
  • Görüntüyü yükler ve önişleme yapar.OpenCV renk kanallarını keras uyumluluğuna değiştirir ve 244×244 piksel olarak yeniden boyutlandırılır.
image = cv2.imread(imagePath)image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)image = cv2.resize(image, (224, 224))
  • Veri ve etiketleri sırayla veri ve etiket listesine ekler.
data.append(image)labels.append(label)
  • veri ve etiket listelerimizi Numpy dizilerine dönüştürür.
data = np.array(data)labels = np.array(labels)
  • One-hot encoding, etkin bir sınıf etiketini ikili dizi öğeleri aracılığıyla işaretlemenin bir yoludur. Örneğin “Paper” dizi ([1, 0, 0]) olabilirken “Scissors” dizi ([0, 0, 1]) olabilir.
lb = LabelBinarizer()labels = lb.fit_transform(labels)
  • Verileri test için %25, eğitim için %75 ‘e ayırdım.
(trainX, testX, trainY, testY) = train_test_split(data, labels,test_size=0.25, stratify=labels, random_state=42)
  • Veri setimizi çoğaltmak için augmentation işlemi uygularız. Farklı açılardan görüntüyü çoğaltır. Eğer farklı görüntülerle çalışacaksanız. Daha İyi bir sonuç alamak için, parametrelerini veya methodunu değiştirebilirsiniz.
trainAug = ImageDataGenerator(rotation_range=20,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.15,horizontal_flip=True,fill_mode="constant")valAug = ImageDataGenerator()
  • Ortalama piksel değeri ayarlanır.
mean = np.array([123.68, 116.779, 103.939], dtype="float32")
  • training/evaluation sırasında görüntüler üretilirken ortalama çıkarma işlemi gerçekleştirilecek şekilde trainAug ve valAug için ortalama özniteliği ayarlar.
trainAug.mean = meanvalAug.mean = mean
  • ResNet101’yi ImageNet ağırlıklarıyla önceden eğitilmiş olarak yüklerken, ağın başını keser.
baseModel = ResNet101(weights="imagenet", include_top=False,input_tensor=Input(shape=(224, 224, 3)))
  • yeni bir headModel oluşturur. ve bunu baseModel ‘e aktarır.
headModel = baseModel.outputheadModel = AveragePooling2D(pool_size=(7, 7))(headModel)headModel = Flatten(name="flatten")(headModel)headModel = Dense(512, activation="relu")(headModel)headModel = Dropout(0.5)(headModel)headModel = Dense(len(lb.classes_), activation="softmax")(headModel)model = Model(inputs=baseModel.input, outputs=headModel)
  • Şimdi baseModel’i, geri yayınlama yoluyla eğitilmemesi için donduracağız.
for layer in baseModel.layers:layer.trainable = Falseprint("[INFO] compiling model...")opt = SGD(lr=1e-4, momentum=0.9, decay=1e-4 / args["epochs"])model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])
  • Modelimizdeki fit_generator işlevine yapılan bir çağrı ağımızı veri artırma ve ortalama çıkarma ile eğitir.
H = model.fit(x=trainAug.flow(trainX, trainY, batch_size=32),steps_per_epoch=len(trainX) // 32,validation_data=valAug.flow(testX, testY),validation_steps=len(testX) // 32,epochs=args["epochs"])
print("[INFO] evaluating network...")
  • Ağımızı test setinde değerlendirip bir sınıflandırma raporu yazdırdıktan sonra, devam edip accuracy/loss eğrilerimizi matplotlib ile çiziyoruz. Çizim, Hat 164 aracılığıyla diske kaydedilir.
predictions = model.predict(x=testX.astype("float32"), batch_size=32)print(classification_report(testY.argmax(axis=1),predictions.argmax(axis=1), target_names=lb.classes_))
N = args["epochs"]plt.style.use("ggplot")plt.figure()plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc")plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc")plt.title("Training Loss and Accuracy on Dataset")plt.xlabel("Epoch #")plt.ylabel("Loss/Accuracy")plt.legend(loc="lower left")plt.savefig(args["plot"])print("[INFO] serializing network...")
  • Keras modelimizi kaydeder.
model.save(args["model"], save_format="h5")
  • satırları etiket ikilileştiricimizi Python’un pickle biçiminde seri hale getirir ve saklar.
f = open(args["label_bin"], "wb")f.write(pickle.dumps(lb))f.close()

Train için kodlarımız bu kadardır. Başta oluşturduğumuz argümanlar sayesinde, kodları colab ‘a tek tek yazmak yerine dosyayı yükleyip dosya ismiyle eğitimimizi başlatacağız. Aşağıdaki kodu colab ‘a çalıştırınca eğitimimiz başlayacaktır.

!python trainresnNET101SGD.py --dataset dataset --model model/activity.model \--label-bin output/lb.pickle --epochs 50

Yukarıda train dosyamızın uzantısını , — dataset dataset kısmı verisetimizin dosyasını, — model model/activity.model kısmı oluşacak modelimizin nereye kaydedileceğini, — label-bin output/lb.pickle pickle dosyamızın kaydedeceği yeri belirtmektedir.

Eğitim sonuçlarınıza göre epoch sayısını da değiştirebilirsiniz. Bu şekilde overfitting ve underfitting ‘ten kaçınmış olursunuz. Eğitim süresini veriseti etkilemektedir. Farklı bir yol deneyip az veri ile de güzel bir sonuç çıkarabilirsiniz. Verinizi azaltarak daha hızlı eğitim yapabilirsiniz. Bu şekilde parametre vb. diğer özelliklerini değiştirerek tekrar eğiterek en iyi sonuca varabilirsiniz.

Eğitim Sonucu Oluşan Değerlerimiz

Aşağıda epoch 30 adım da eğitim sonucu çıkardığı değerler gözükmektedir.

Eğitim Sonucu Oluşan Grafiğimiz

  • Aşağıda Yapılan eğitim sonucu validation ve train kaybını aşağı doğru indirdiğini ve doğruluk derecesini yükselttiğini görüyoruz.

Tahmin Etme veye Test Etme Dosyası

from tensorflow.keras.models import load_modelfrom collections import dequeimport numpy as npimport argparseimport pickleimport cv2
  • model: Önceki eğitim adımımızdan oluşturulan girdi modeline giden yol.
  • label-bin: Önceki komut dosyası tarafından oluşturulan serileştirilmiş pickle biçimli etiket ikilileştiricisinin yolu.
  • input: Video sınıflandırması için bir giriş videosuna giden yol.
  • output: Diske kaydedilecek çıktı videomuzun yolu.
  • size: Dönen ortalama için kuyruğun maksimum boyutu (varsayılan olarak 128). Daha sonraki örnek sonuçlarımızdan bazıları için boyutu 1 olarak ayarlayacağız, böylece ortalama alınmaz.
ap = argparse.ArgumentParser()ap.add_argument("-m", "--model", required=True,help="path to trained serialized model")ap.add_argument("-l", "--label-bin", required=True,help="path to  label binarizer")ap.add_argument("-i", "--input", required=True,help="path to our input video")ap.add_argument("-o", "--output", required=True,help="path to our output video")ap.add_argument("-s", "--size", type=int, default=128,help="size of queue for averaging")args = vars(ap.parse_args())
  • model ve etiketleri binarizer yükler.
print("[INFO] loading model and label binarizer...")model = load_model(args["model"])lb = pickle.loads(open(args["label_bin"], "rb").read())
  • Ortalama çıkarma değerimizi ayarlar.
mean = np.array([123.68, 116.779, 103.939][::1], dtype="float32")
  • Dönen tahmin ortalamamızı uygulamak için bir deque kullanacağız. Deque’imiz Q, args [“size”] değerine eşit bir maxlen ile başlatılır.
Q = deque(maxlen=args["size"])
  • Girdi video dosyası akışımıza bir işaretçi yakalar. Video akışımızdan kareleri
  • okumak için OpenCV’deki VideoCapture sınıfını kullanıyoruz.
vs = cv2.VideoCapture(args["input"])writer = None(W, H) = (None, None)
  • Döngü video sınıflandırmamıza başlar. Çerçeve oluşur. Eğer oluşmuyorsa videonun sonuna gelmiş oluruz. Ve döngü durur.
while True:      (grabbed, frame) = vs.read()      if not grabbed:          break
  • Gerekirse çerçeve boyutu ayarlanır.
if W is None or H is None:        (H, W) = frame.shape[:2]
output = frame.copy()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
  • Çerceve 224×224 ‘de yeniden boyutlandırılır.
frame = cv2.resize(frame, (224, 224)).astype("float32")frame -= mean
preds = model.predict(np.expand_dims(frame, axis=0))[0]Q.append(preds)
  • Parçalara ayrılan bu çizgiler, ortalama tahminler arasında en büyük karşılık gelen olasılığa sahip etiketi bulur. Yani çıktıda yazdırılan en yüksek yüzdeyi gösterilir. Eğer argmax değil argmin olsaydı. Bu sefer endüşük değeri yaklamaya çalışacaktı.
results = np.array(Q).mean(axis=0)i = np.argmax(results)label = lb.classes_[0]
  • Ekrana sınıflar ve doğruluk yüzdelerini yazar.
text = "Activity Class: {}".format(label) +' '+ '{}%'.format(round(preds[0]*100,2))
  • Hangi işlemin en yüksek verdiğini bize göstermesi için eşik değeri belirledim. doğruluk %85 üzerindeyse yazı boyutu ve değerleri değişecektir.
if float(round(preds[0]*100,2)) > 85:   cv2.putText(output, text, (35, 90), cv2.FONT_HERSHEY_SIMPLEX,1.5, (0, 255, 0), thickness=2)else:     cv2.putText(output, text, (35, 90), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 255), thickness=2)i = np.argmax(results)label1 = lb.classes_[1]text = "Activity Class: {}".format(label1) +'  '+ '{}%'.format(round(preds[1]*100,2))if float(round(preds[1]*100,2)) > 85:    cv2.putText(output, text, (35, 160), cv2.FONT_HERSHEY_SIMPLEX,1.5, (0, 255, 0), thickness=2)else:     cv2.putText(output, text, (35, 160), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 255), thickness=2)label2 = lb.classes_[2]# draw the activity on the output frametext = "Activity Class: {}".format(label2) +'  '+ '{}%'.format(round(preds[2]*100,2))if float(round(preds[2]*100,2)) > 85:     cv2.putText(output, text, (35, 220), cv2.FONT_HERSHEY_SIMPLEX,1.5, (0, 255, 0), thickness=2)else:    cv2.putText(output, text, (35, 220), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 255), thickness=2)
  • Video yazıcıyı başlatır. writer kısmından video özelliklerini değiştire bilirsiniz. Mesela 30 değerini 8 yaparsak video çıktısı daha yavaş olacaktır.
if writer is None:   fourcc = cv2.VideoWriter_fourcc(*"MJPG")   writer = cv2.VideoWriter(args["output"], fourcc, 30,   (W, H), True)print("[INFO] cleaning up...")writer.release()vs.release()

Kaynaklar

https://www.pyimagesearch.com/2019/07/15/video-classification-with-keras-and-deep-learning/