Python——基于pytorch的3D视频动作识别

        pytorch初接触——唐宇迪教教程的3D卷积视频动作识别。接触之后,发现pytorch比tensorflow的用户体验要好一点点,TF由于兼容性问题,从其他地方拿到代码,第一感觉就是跑不起来,很多代码都是基于TF1.x写的,跟2.x一堆不兼容问题。由此开始研究pytorch,后面用的顺手可能直接转pytorch,目前这个架构开始追赶TF。

        一、pytorch基本步骤

        python每个架构都有大致的操作步骤,学习过程中,查找API资料以及通过代码摸索,我总结了pytorch的基本操作步骤,如下:

        ①.定义model
        model = Model
        ②.定义参数、优化器、loss
        train_params = [{'params': params(model), 'lr': lr}]
        criterion = nn.CrossEntropyLoss()#loss function
        optimizer = optim.SGD(train_params,lr=lr,momentum=0.9,weight_decay=5e-4)
        ③.to decive
        model.to(device)
        criterion.to(device)
        ④.开始epoch循环
        for input, target in dataset:
            model.trian()
            inputs = Variable()
            labels = Varibale()
            optimizer.zero_grad()
            output = model(input)
            pre = nn.activaiton()
            loss = loss_fn(output, target)
            loss.backward()
            optimizer.step()

二、网络基本架构

        开始前需要配置环境,我使用的是torch1.13 + cuda11.6,GPU版本,安装后如果要知道是否能使用GPU进行计算,就使用这句代码:torch.cuda.is_available() ,输出为True即表示GPU可以使用。

        整个代码由几个部分组成:①.dataloader进行视频数据的处理;②train进行epoch训练;③inference进行预测。这个代码说是由一篇论文复现的,但我没有去看论文,model部分的代码直接复制唐宇迪源码(如有倾权,一定删除)。      

        1.dataloader数据处理

        这部分我觉得是重点,理解整个代码,在这里花费了很长时间。这部分的思路是:定义基本参数,例如截取视频帧的数量等——判断输出路径文件夹是否建立、文件夹中的jpg是否符合情况——处理视频数据。preprocess(self)这个函数的作用是判断有没有建立train\val\test三个文件夹,如果有没有的话就使用os.mkdir函数建立这三个文件夹,用于存放处理后的jpg文件。process_video(self,video,action_name,save_dir)这个函数就是正式开始处理视频,EXTRACT_FREQUENCY这个参数的目的是为了每隔4个frame截取一张图片,如果整个视频截取小于16张frame,那EXTRACT_FREQUENCY-1,直到能够凑够16张frame以上为止。以下为这两个函数源码:

    def preprocess(self):
        if not os.path.exists(self.output_dir):
            os.mkdir(self.output_dir)
            os.mkdir(os.path.join(self.output_dir,'train'))
            os.mkdir(os.path.join(self.output_dir, 'val'))
            os.mkdir(os.path.join(self.output_dir, 'test'))

        for file in os.listdir(self.root_dir):
            file_path = os.path.join(self.root_dir,file)
            video_files = [name for name in os.listdir(file_path)]
            train_and_valid, test = train_test_split(video_files,test_size=0.2,random_state=42)
            train,val = train_test_split(train_and_valid,test_size=0.2,random_state=42)

            train_dir = os.path.join(self.output_dir,'train',file)
            # print("train_dir is ",train_dir)
            val_dir = os.path.join(self.output_dir,'val',file)
            test_dir = os.path.join(self.output_dir,'test',file)
            if not os.path.exists(train_dir):
                os.mkdir(train_dir)
            if not os.path.exists(val_dir):
                os.mkdir(val_dir)
            if not os.path.exists(test_dir):
                os.mkdir(test_dir)

            for video in train:
                self.process_video(video,file,train_dir)
            for video in val:
                self.process_video(video, file, val_dir)
            for video in test:
                self.process_video(video, file, test_dir)
        print('Preprocessing finished')

    def process_video(self,video,action_name,save_dir):
        # video name:v_ApplyEyeMakeup_g01_c01.avi
        video_filename = video.split('.')[0]

        if not os.path.exists(os.path.join(save_dir,video_filename)):
            os.mkdir(os.path.join(save_dir,video_filename))
        capture = cv2.VideoCapture(os.path.join(self.root_dir,action_name,video))
        frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
        frame_width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
        # Make sure splited video has at least 16 frames
        EXTRACT_FREQUENCY = 4
        if frame_count // EXTRACT_FREQUENCY <= 16:
            EXTRACT_FREQUENCY -= 1
            if frame_count // EXTRACT_FREQUENCY <= 16:
                EXTRACT_FREQUENCY -= 1
                if frame_count // EXTRACT_FREQUENCY <= 16:
                    EXTRACT_FREQUENCY -= 1

        count = 0
        i = 0
        retaining = True

        while (count < frame_count and retaining):
            retaining, frame = capture.read()
            if frame is None:
                continue
            # resize the img and save to save_dir
            if count % EXTRACT_FREQUENCY == 0:
                if (frame_height != self.resize_height) or (frame_width != self.resize_width):
                    frame = cv2.resize(frame, (self.resize_width, self.resize_height))
                cv2.imwrite(filename=os.path.join(save_dir, video_filename, '0000{}.jpg'.format(str(i))), img=frame)
                i += 1
            count += 1

        # Release the VideoCapture once it is no longer needed
        capture.release()

        2.train进行训练

        这里的模型采用C3D_model,3D卷积与2D卷积的最大区别在于多了一个时间维度T,是将视频拆解为多个frame,然后同时输出model中,将每一帧溶入到一个维度中,也就是现在的卷积核变为mxnxT,T对应多个图像。这里整个模型的参数如下:

nEpochs = 30#这里我只采用30个,跑一个需要10min,跑完这30个我花了5.5h,精度才到0.58
model = C3D_model.C3D(num_classes=num_classes,pretrained=False)
criterion = nn.CrossEntropyLoss()#loss function
optimizer = optim.SGD(train_params,lr=lr,momentum=0.9,weight_decay=5e-4)
model.to(device)
criterion.to(device)

        3.inference进行预测

        在执行这个代码时,程序在下面这里报错,信息显示在forward(inputs)这里有问题。

with torch.no_grad():
    outputs = model.forward(inputs)

        具体是view这里报错,查了网上的解决方案,需要在view前面加上.contiguous().这一段,随后就可以正常运行了。

x = x.contiguous().view(-1, 8192)

        我这边使用的配置是3060+6g的笔记本跑这个程序,30个epoch也只有0.58的准确率,用test数据测试的结果确实不是100%准确,如下图,结果应该是ApplyEyeMakeup,但识别到的是ApplyLipstick。

Python——基于pytorch的3D视频动作识别_第1张图片

 

整个项目的代码和下载链接在:https://download.csdn.net/download/wenpeitao/87346700

你可能感兴趣的:(pytorch,python,深度学习)