Two-stream convolutional networks for action recognition in videos (+ code review)
Abstract
Action regonition을 비디오에서 사용하고 하는 시도를 본 논문에서 시도함
- 1.spatial and temporal network 를 통합하는 two-stream ConvNet
- 2.multi-frame dense optical flow 로 훈련된 ConvNet
- 3.서로 다른 두가지 data set에 multi-task learning을 적용함으로써 학습데이터양과 성능향상을 이끌어냈다.
1. Introduction
정적인 Image classification 과 비교해서 video 의 temporal 한 정보는 추가적인 정보를 제공한다.
또 video data 는 (연속된 프레임의 집합이기 때문에) 자연적인 data augmentation(jittering) 을 제공한다.
(Cf. 다음논문에서도 발견한 공통점인데 , 아마도 video action recognition 에서는 video 의 temporal 한 정보를 얼마냐 잘 보존하면서 활용할 수 있는지가 포인트였던 것 같다.)
이전에도 video action recog 에 대한 시도들이 있었는데 성능이 별로 좋지 않았다. (정확히 어떤 부분이 문제였는지는 알려주지 않았다. —> 너무 옛날논문이라 굳이 찾아보지는 않았다. 아마도 temporal 한 정보의 손실이 문제 아니였을까.)
그래서 본 논문에서는 late fusion 을 이용한 two separate recognition streams 를 구조로한 아키텍쳐를 구성했다.
Spatial stream(공간적 정보) : 정지된 프레임에서 action recognition ( 정적 이미지에서 바로 recognition 하는 것과 동일 )
Temporal stream(시간적 정보) : action 을 dense optical flow 의 형태로써 인식하도록 학습된다.
앞에서도 이야기했지만 본 논문에서 주의깊게 보아야 할 것은 temporal 부분이다.
이렇게 공간/시간으로 나누는 것으로부터 spatial stream은 이미지 넷과 같은 이미지에 대한 annotation 데이터로 사전 학습시킬 수 있는 장점을 얻을 수 있다.
(Bounding box나 semantic seg와 같이 태그를 달아주는 것 )(아마도 정지 프레임에서 sptatial stream을 실행하다 보니 이미지를 pre-train 해서 사용 하는 듯 하다)
2. Two stream architecture for video recognition
video 는 spatial 그리고 temporal 로 decomposed 된다.
앞에서도 간단하게 설명했지만 다시한번 temporal 과 spatial 부분에 대한 설명을 다시한번 해보자면
- spatial part : video 에서 묘사된 장면과 물체에 대한 정보이다.
- Temporal part : 카메라(관찰자)와 물체의 움직임에 대한 정보를 담고있다.
이처럼 두파트로 나뉘게 되는데, 본 논문에서의 아키텍쳐도 이를 이용하기위해서
two stream 으로 나눈다.

( 이처럼 아키텍쳐를 두개로 나누었다. 마지막 layer 에서는 late fusion 을 사용해서 합친다. )
Spatial stream ConvNet
(Figure 1 위쪽 stream) 논문에서는 먼저 spatial stream 에 대한 부분을 먼저 다루고있다. introduction 에서도 설명했듯이 spatial 부분은 단일 프레임( static image를 사용한다고 이해해도 괜찮다.)을 사용해서 정지 이미지에서의 action recoginition 을 수행한다.
결국 이미지에서 action recognition 을 하는 것과 마찬가지이고 이는 이미 pre-train 된 모델들을 사용해서 구하게 된다.
(AlexNet을 사용한다는 것으로 보아 실제로 오래된 논문인 것 같다.)
(Cf.)

6. Evaluation 을 가지고 와봤는데 여기서도 알 수 있듯이 spatial stream 에서는 마지막 layer 을 제외하고 다른 layer 들을 fix 시키는 pre-train 된 모델을 가지고 학습하게 되는걸 볼 수 있다.
3. opitcal flow ( temporal stream )
temporal recognition stream 에서는 optical flow 를 사용하게 된다.
본 논문의 모델은 연속된 프레임들의 optical flow displacement field 를 stacking 한 것을 input 으로 받게 된다.
아마 본 논문의 가장 중요한 부분인 것 같은데, 다다음 논문 (quo vadis,action recognition? A new model and the kinetics dataset ) 을 참고하면 알 수 있듯이 이전까지 모델들은 optical flow 를 다른 방식(더 비효율적인) 방식으로 계산하고, 이에 따라서 모델에 활용하기 어려웠는데 본 논문에서 그 점을 고쳤다고 확인 했었던 것 같다.
( 추후에 위 논문을 리뷰할때 다시한번 설명하도록 하겠다 )

(a)(b) 영상에서 손 움직임.
(C) optical flow 클로즈업
(d) (e) 수직/수평 성분의 displace vector
3.1 ConvNet input configurations
Optical flow stacking
앞서 본 논문에서는 opitcal flow 를 stacking 하는 방식을 사용해서 input 으로 받아 준다고 하였다.
dense optical flow 는 연속된 frame t 와 t+1 사이의 displacement vector 의 집합으로 볼 수 있다.
프레임 t 의 점 (u,v) 에서 displacement vet 를 d^t(u,v) 라고 하자. 이는 점 u,v 에서 다음 프레임 t+1 에서 물체에 대응되는 동일 점과의 displacement vector 이다. 물체를 추적하기 위함이고, 또한 동일한 픽셀을 대응시키면, 항상 변위는 0이기 때문에 암묵적으로 저런 의미를 내포하고 있다고 한다.
프레임의 움직임을 나타내기 위해서 L개의 연속 프레임에서의 수직/수평 성분을 나누어서
수직성분 d^tx - 수평성분 d^ty 로 나타내었다. 또 프레임의 sequence 사이의 행동을 인식하기 위해서 총 L 개의 연속된 프레임을 각각 수직성분 d^tx 와 d^ty 를 쌓아서 2L 채널로 만들었다.
이에대한
조금 더 자세한 설명은 여기 서 확인 할 수 있다.
간단하게 요약하자면 프레임 사이의 optical flow 를 구하기위해
수직 / 수평성분의 변위를 따로 구한뒤 이를 합쳐준 것이다.
Trajectory stacking

Trajectory stacking 은 optical flow 의 수정된 버전으로
figure 3 의 왼쪽부분(기존 opitcal flow) 방식에서는 동일한 위치에서 계산을 해 나가지만
trajectory stacking( 오른쪽 그림) 방식에서는 이동한 위치에서 부터 계산하게 된다.

Pk의 값을 Pk-1 값에서 계속해서 갱신해 나가면서 구하는 것을 확인 할 수 있다.
4. Multi task learning
Spatial ConvNet 은 기존의 image classification 을 통해서 학습하면 되기 때문에 다양한 데이터셋이 존재하고 모델 성능도 어느정도 검증이 되어있지만,
temporal ConvNet은 오로지 video data를 가지고만 학습이 되기 때문에 데이터셋의 부족으로 overfitting 이 생긴다. 이를 해결하고자 본 논문에서는 2개의 데이터셋 (UCF-101 / HMDB_51) 을 사용하여 학습을 진행하였다.
2개의 데이터셋의 클래스가 서로 다르기 때문에 multi-task learning 을 사용하게 되었는데,

위 그림과 같이 데이터셋 별로 다른 softmax 를 통해서 클래스를 분류하고 각각의 loss 를 더해 업데이트 해주는 방식을 사용한다.
6. Evaluation

Spatial conv
맨처음 2 - spatial conv 에서 잠깐 짚고 넘어간 부분인데, last layer 만 고정시키고 fine-tuning 하는게 성능이 가장 좋았다.
Temporal conv
몇개를 stacking 할 것인지 구하는게 포인트였던 것 같다. L=1 ( stacking 하지 않은 것) 보다 더 많이 쌓은 (그니까 stacking 했을 때 ) 모두 성능이 더 좋은 것으로 미루어 보아 stacking하는게 효과가 있었던 것 같다. 또한 mean subtraction 을 사용했을때 모두 더 성능이 향상되었고, trajectory stacking 보다는 기존 optical flow 를 사용하는게 더 성능이 좋았다.
또 multi-task learning 을 사용했을때가 더 성능이 좋았다.
(table2 )
7. Conclusion and directions for improvement
spatial 과 temporal 을 구분한 모델을 제안했다.
Temporal model 은 hand-crafting 없이 학습할 수 있었다.
( 아마도 end-to-end 처럼 모델이 모두 처리해준다는 뜻인듯)
8. 뒤의 논문과 이어질 포인트 다시 재정리
Code review
따로 깃허브에 논문 공식적인 깃허브는 존재하지 않고
본 논문과 타 논문들을 합쳐서 만든 코드들은 존재했다.
https://github.com/jeffreyyihuang/two-stream-action-recognition/blob/master/motion_cnn.py
Spatial CNN 은 기존 ResNet 코드와 비슷해 따로 리뷰하지 않고
본 논문에서 중요한 montion CNN ( temporal ) 리뷰 해보려고 한다.
Motion cnn
- Input data of motion cnn is a stack of optical flow images which contained 10 x-channel and 10 y-channel images, So it's input shape is (20, 224, 224) which can be considered as a 20-channel image.
- In order to utilize ImageNet pre-trained weight on our model, we have to modify the weights of the first convolution layer pre-trained with ImageNet from (64, 3, 7, 7) to (64, 20, 7, 7).
- In [2] Wang provide a method called **Cross modality pre-
** to do such weights shape transform. He first average the weight value across the RGB channels and replicate this average by the channel number of motion stream input( which is 20 is this case)
def main():
global arg
arg = parser.parse_args()
print arg
#Prepare DataLoader
data_loader = dataloader.Motion_DataLoader(
BATCH_SIZE=arg.batch_size,
num_workers=8,
path='/home/ubuntu/data/UCF101/tvl1_flow/',
ucf_list='/home/ubuntu/cvlab/pytorch/ucf101_two_stream/github/UCF_list/',
ucf_split='01',
in_channel=10,
)
train_loader,test_loader, test_video = data_loader.run()
#Model
model = Motion_CNN(
# Data Loader
train_loader=train_loader,
test_loader=test_loader,
# Utility
start_epoch=arg.start_epoch,
resume=arg.resume,
evaluate=arg.evaluate,
# Hyper-parameter
nb_epochs=arg.epochs,
lr=arg.lr,
batch_size=arg.batch_size,
channel = 10*2,
test_video=test_video
)
#Training
model.run()
dataloader 을 할때 optical flow 가 계산되어 있는 데이터를 https://github.com/feichtenhofer/twostreamfusion 에서 가지고 온 것 부터 시작한다. ( 어떻게 optical flow 를 계산하는지는 위 깃허브를 참고)
x,y 채널 각각 10개씩 해서 총 20개 채널 이미지를 input 으로 받아온다.
class Motion_CNN():
def __init__(self, nb_epochs, lr, batch_size, resume, start_epoch, evaluate, train_loader, test_loader, channel,test_video):
self.nb_epochs=nb_epochs
self.lr=lr
self.batch_size=batch_size
self.resume=resume
self.start_epoch=start_epoch
self.evaluate=evaluate
self.train_loader=train_loader
self.test_loader=test_loader
self.best_prec1=0
self.channel=channel
self.test_video=test_video
def build_model(self):
print ('==> Build model and setup loss and optimizer')
#build model
self.model = resnet101(pretrained= True, channel=self.channel).cuda()
#print self.model
#Loss function and optimizer
self.criterion = nn.CrossEntropyLoss().cuda()
self.optimizer = torch.optim.SGD(self.model.parameters(), self.lr, momentum=0.9)
self.scheduler = ReduceLROnPlateau(self.optimizer, 'min', patience=1,verbose=True)
모델 부분을 살펴보면, build_model 에서 resnet 을 사용해서 모델을 구성했는데
사실 본 논문에서는 resnet 을 통해서 구현하지는 않았다 ( 논문이 나오고 resnet이 나왔다)
본 논문보다 더 이후에 만들어진 코드이기 때문에 편의상 resnet 을 사용한다고 생각하자.
Cross entropy loss 를 통해서 loss 를 계산해주고 loss.cuda() 를 통해서 GPU 에 할당해준다.
( 원래 gpu 에 할당할때 if 'cuda' available --> device = 'GPU' --> model.to(DEVICE) 이런식으로 코드를 작성했던 것 같은데
바로 .cuda() 통해서 loss function 만 바로 할당할 수도 있었다. ( cf. model.cuda = 모델 전체 gput 이동시켜 계산))
SGD 를 통해 optimizer 하였고
ReduceLROnPlateau() 라는 schedulare 를 통해 학습을 최대화 하고자 하였다.
def resume_and_evaluate(self):
if self.resume:
if os.path.isfile(self.resume):
print("==> loading checkpoint '{}'".format(self.resume))
checkpoint = torch.load(self.resume)
self.start_epoch = checkpoint['epoch']
self.best_prec1 = checkpoint['best_prec1']
self.model.load_state_dict(checkpoint['state_dict'])
self.optimizer.load_state_dict(checkpoint['optimizer'])
print("==> loaded checkpoint '{}' (epoch {}) (best_prec1 {})"
.format(self.resume, checkpoint['epoch'], self.best_prec1))
else:
print("==> no checkpoint found at '{}'".format(self.resume))
if self.evaluate:
self.epoch=0
prec1, val_loss = self.validate_1epoch()
return
def run(self):
self.build_model()
self.resume_and_evaluate()
cudnn.benchmark = True
for self.epoch in range(self.start_epoch, self.nb_epochs):
self.train_1epoch()
prec1, val_loss = self.validate_1epoch()
is_best = prec1 > self.best_prec1
#lr_scheduler
self.scheduler.step(val_loss)
# save model
if is_best:
self.best_prec1 = prec1
with open('record/motion/motion_video_preds.pickle','wb') as f:
pickle.dump(self.dic_video_level_preds,f)
f.close()
save_checkpoint({
'epoch': self.epoch,
'state_dict': self.model.state_dict(),
'best_prec1': self.best_prec1,
'optimizer' : self.optimizer.state_dict()
},is_best,'record/motion/checkpoint.pth.tar','record/motion/model_best.pth.tar')
run 메서드를 크게 살펴보면 build_model 로 기본 모델 만들어 준 뒤
loss 계산시 체크포인트마다 값 저장해준다(1epoch 마다 저장).
좀 더 주의깊게 봐야할 부분이라고 한다면
is_best = prec1 > self.best_prec1
(prec1 은 현재 validate_1epoch 값 / self.best_prec1 은 학습(epoch)을 진행하면서 나온 best prec 값 )
if is_best == True --> best_prec1 를 재설정하고 기록한다.
def train_1epoch(self):
print('==> Epoch:[{0}/{1}][training stage]'.format(self.epoch, self.nb_epochs))
batch_time = AverageMeter()
data_time = AverageMeter()
losses = AverageMeter()
top1 = AverageMeter()
top5 = AverageMeter()
#switch to train mode
self.model.train()
end = time.time()
# mini-batch training
progress = tqdm(self.train_loader)
for i, (data,label) in enumerate(progress):
# measure data loading time
data_time.update(time.time() - end)
label = label.cuda(async=True)
input_var = Variable(data).cuda()
target_var = Variable(label).cuda()
# compute output
output = self.model(input_var)
loss = self.criterion(output, target_var)
# measure accuracy and record loss
prec1, prec5 = accuracy(output.data, label, topk=(1, 5))
losses.update(loss.data[0], data.size(0))
top1.update(prec1[0], data.size(0))
top5.update(prec5[0], data.size(0))
# compute gradient and do SGD step
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()
info = {'Epoch':[self.epoch],
'Batch Time':[round(batch_time.avg,3)],
'Data Time':[round(data_time.avg,3)],
'Loss':[round(losses.avg,5)],
'Prec@1':[round(top1.avg,4)],
'Prec@5':[round(top5.avg,4)],
'lr': self.optimizer.param_groups[0]['lr']
}
record_info(info, 'record/motion/opf_train.csv','train')
train 부분과 validation 부분은 거의 유사해서 train 부분만 리뷰하고 코드리뷰를 마치고자한다.
( tqdm --> 프로세스 진행 상태 확인 가능하게 )
for 문을 돌면서
data loading time ( measure elapsed time)
compute output
measure accuracy and record loss
compute gradient and do SGD step
정도 수행하고
measure accuracy and record loss 에서 accuracy 를 argmax 대신 top 5 를 사용했다는 점이 특이했다.
느낀점
일단 코드 리뷰 자체가 이렇게 글로 쓰는건 처음이기도 했고,
내가 예상했던 코드 리뷰의 중심은 본 논문에서 temporal stream 를 자세히 리뷰하는 것 이였는데
생각과 달리 이미 optical flow 를 계산되어있는걸 가져다 쓰면서 optical flow 구하는 코드 , 그리고
본 논문만 가지고 작성된 코드가 아니라 L=1,5,10 일때 따로 코드가 구현되어 있지 않아서 좀 아쉬웠다.
전체적으로 내가 생각한 느낌의 코드는 없고 그냥그냥 다른 모델과 비슷하게 학습하는 방법만 있어서 조금 아쉬웠다?
논문 리뷰할때 이해한 것 보다 정리하는게 더 까다로워서 어려웠고
아는걸 글로 풀어내는게 어려웠다. 중간에 spatial 부분에서 opical flow 에 대한 자세한 이야기를
조금 뭉개고 진행했는데, 일단은 내가 optical flow 를 정확하게 잘 알지도 못할 뿐더러, 실험 결과가 그닥
좋아보이지는 않아서 생략했다.
논문 리뷰, 코드리뷰 둘다 조금 엉성하게 된 것 같은데
어느부분에서 힘을 줘서 리뷰를 해야할지 처음부터 정하고 글을 썼으면 더 깔끔했을 것 같다는
느낌이 들었고 , 코드가 직접 들어있는 논문 가지고 리뷰하면 좀 더 자세하게 볼 수 있을 것 같았다.
논문 읽을때마다 느끼는건데 expreimental / implement 부분은 읽기도 까다로운것 같다.
다른 논문들을 읽은지 사실 오래 되었는데 이제 처음 리뷰룰 작성한다.
다음 논문은 learning spatiotemporal feature with 3d convolutional networks 와 quo vadis, action recgonition? a new model and kinetics dataset 두개를 엮어서 총 3개(이번 논문까지 포함) 해서 한꺼번에 진행해보려고 한다.
더 확인해봐야 할 부분
1. argpase
2. optical flow 계산
3. checkpoint 어떻게 찍히는 지 직접 확인
4. multi-task learning
<전체 코드>
import numpy as np
import pickle
from PIL import Image
import time
import tqdm
import shutil
from random import randint
import argparse
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transforms
import torchvision.models as models
import torch.nn as nn
import torch
import torch.backends.cudnn as cudnn
from torch.autograd import Variable
from torch.optim.lr_scheduler import ReduceLROnPlateau
from utils import *
from network import *
import dataloader
os.environ["CUDA_VISIBLE_DEVICES"] = "1"
parser = argparse.ArgumentParser(description='UCF101 motion stream on resnet101')
parser.add_argument('--epochs', default=500, type=int, metavar='N', help='number of total epochs')
parser.add_argument('--batch-size', default=64, type=int, metavar='N', help='mini-batch size (default: 64)')
parser.add_argument('--lr', default=1e-2, type=float, metavar='LR', help='initial learning rate')
parser.add_argument('--evaluate', dest='evaluate', action='store_true', help='evaluate model on validation set')
parser.add_argument('--resume', default='', type=str, metavar='PATH', help='path to latest checkpoint (default: none)')
parser.add_argument('--start-epoch', default=0, type=int, metavar='N', help='manual epoch number (useful on restarts)')
def main():
global arg
arg = parser.parse_args()
print arg
#Prepare DataLoader
data_loader = dataloader.Motion_DataLoader(
BATCH_SIZE=arg.batch_size,
num_workers=8,
path='/home/ubuntu/data/UCF101/tvl1_flow/',
ucf_list='/home/ubuntu/cvlab/pytorch/ucf101_two_stream/github/UCF_list/',
ucf_split='01',
in_channel=10,
)
train_loader,test_loader, test_video = data_loader.run()
#Model
model = Motion_CNN(
# Data Loader
train_loader=train_loader,
test_loader=test_loader,
# Utility
start_epoch=arg.start_epoch,
resume=arg.resume,
evaluate=arg.evaluate,
# Hyper-parameter
nb_epochs=arg.epochs,
lr=arg.lr,
batch_size=arg.batch_size,
channel = 10*2,
test_video=test_video
)
#Training
model.run()
class Motion_CNN():
def __init__(self, nb_epochs, lr, batch_size, resume, start_epoch, evaluate, train_loader, test_loader, channel,test_video):
self.nb_epochs=nb_epochs
self.lr=lr
self.batch_size=batch_size
self.resume=resume
self.start_epoch=start_epoch
self.evaluate=evaluate
self.train_loader=train_loader
self.test_loader=test_loader
self.best_prec1=0
self.channel=channel
self.test_video=test_video
def build_model(self):
print ('==> Build model and setup loss and optimizer')
#build model
self.model = resnet101(pretrained= True, channel=self.channel).cuda()
#print self.model
#Loss function and optimizer
self.criterion = nn.CrossEntropyLoss().cuda()
self.optimizer = torch.optim.SGD(self.model.parameters(), self.lr, momentum=0.9)
self.scheduler = ReduceLROnPlateau(self.optimizer, 'min', patience=1,verbose=True)
def resume_and_evaluate(self):
if self.resume:
if os.path.isfile(self.resume):
print("==> loading checkpoint '{}'".format(self.resume))
checkpoint = torch.load(self.resume)
self.start_epoch = checkpoint['epoch']
self.best_prec1 = checkpoint['best_prec1']
self.model.load_state_dict(checkpoint['state_dict'])
self.optimizer.load_state_dict(checkpoint['optimizer'])
print("==> loaded checkpoint '{}' (epoch {}) (best_prec1 {})"
.format(self.resume, checkpoint['epoch'], self.best_prec1))
else:
print("==> no checkpoint found at '{}'".format(self.resume))
if self.evaluate:
self.epoch=0
prec1, val_loss = self.validate_1epoch()
return
def run(self):
self.build_model()
self.resume_and_evaluate()
cudnn.benchmark = True
for self.epoch in range(self.start_epoch, self.nb_epochs):
self.train_1epoch()
prec1, val_loss = self.validate_1epoch()
is_best = prec1 > self.best_prec1
#lr_scheduler
self.scheduler.step(val_loss)
# save model
if is_best:
self.best_prec1 = prec1
with open('record/motion/motion_video_preds.pickle','wb') as f:
pickle.dump(self.dic_video_level_preds,f)
f.close()
save_checkpoint({
'epoch': self.epoch,
'state_dict': self.model.state_dict(),
'best_prec1': self.best_prec1,
'optimizer' : self.optimizer.state_dict()
},is_best,'record/motion/checkpoint.pth.tar','record/motion/model_best.pth.tar')
def train_1epoch(self):
print('==> Epoch:[{0}/{1}][training stage]'.format(self.epoch, self.nb_epochs))
batch_time = AverageMeter()
data_time = AverageMeter()
losses = AverageMeter()
top1 = AverageMeter()
top5 = AverageMeter()
#switch to train mode
self.model.train()
end = time.time()
# mini-batch training
progress = tqdm(self.train_loader)
for i, (data,label) in enumerate(progress):
# measure data loading time
data_time.update(time.time() - end)
label = label.cuda(async=True)
input_var = Variable(data).cuda()
target_var = Variable(label).cuda()
# compute output
output = self.model(input_var)
loss = self.criterion(output, target_var)
# measure accuracy and record loss
prec1, prec5 = accuracy(output.data, label, topk=(1, 5))
losses.update(loss.data[0], data.size(0))
top1.update(prec1[0], data.size(0))
top5.update(prec5[0], data.size(0))
# compute gradient and do SGD step
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()
info = {'Epoch':[self.epoch],
'Batch Time':[round(batch_time.avg,3)],
'Data Time':[round(data_time.avg,3)],
'Loss':[round(losses.avg,5)],
'Prec@1':[round(top1.avg,4)],
'Prec@5':[round(top5.avg,4)],
'lr': self.optimizer.param_groups[0]['lr']
}
record_info(info, 'record/motion/opf_train.csv','train')
def validate_1epoch(self):
print('==> Epoch:[{0}/{1}][validation stage]'.format(self.epoch, self.nb_epochs))
batch_time = AverageMeter()
losses = AverageMeter()
top1 = AverageMeter()
top5 = AverageMeter()
# switch to evaluate mode
self.model.eval()
self.dic_video_level_preds={}
end = time.time()
progress = tqdm(self.test_loader)
for i, (keys,data,label) in enumerate(progress):
#data = data.sub_(127.353346189).div_(14.971742063)
label = label.cuda(async=True)
data_var = Variable(data, volatile=True).cuda(async=True)
label_var = Variable(label, volatile=True).cuda(async=True)
# compute output
output = self.model(data_var)
# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()
#Calculate video level prediction
preds = output.data.cpu().numpy()
nb_data = preds.shape[0]
for j in range(nb_data):
videoName = keys[j].split('-',1)[0] # ApplyMakeup_g01_c01
if videoName not in self.dic_video_level_preds.keys():
self.dic_video_level_preds[videoName] = preds[j,:]
else:
self.dic_video_level_preds[videoName] += preds[j,:]
#Frame to video level accuracy
video_top1, video_top5, video_loss = self.frame2_video_level_accuracy()
info = {'Epoch':[self.epoch],
'Batch Time':[round(batch_time.avg,3)],
'Loss':[round(video_loss,5)],
'Prec@1':[round(video_top1,3)],
'Prec@5':[round(video_top5,3)]
}
record_info(info, 'record/motion/opf_test.csv','test')
return video_top1, video_loss
def frame2_video_level_accuracy(self):
correct = 0
video_level_preds = np.zeros((len(self.dic_video_level_preds),101))
video_level_labels = np.zeros(len(self.dic_video_level_preds))
ii=0
for key in sorted(self.dic_video_level_preds.keys()):
name = key.split('-',1)[0]
preds = self.dic_video_level_preds[name]
label = int(self.test_video[name])-1
video_level_preds[ii,:] = preds
video_level_labels[ii] = label
ii+=1
if np.argmax(preds) == (label):
correct+=1
#top1 top5
video_level_labels = torch.from_numpy(video_level_labels).long()
video_level_preds = torch.from_numpy(video_level_preds).float()
loss = self.criterion(Variable(video_level_preds).cuda(), Variable(video_level_labels).cuda())
top1,top5 = accuracy(video_level_preds, video_level_labels, topk=(1,5))
top1 = float(top1.numpy())
top5 = float(top5.numpy())
return top1,top5,loss.data.cpu().numpy()
if __name__=='__main__':
main()