Spaces:
Runtime error
Runtime error
| import torch | |
| import torch.nn as nn | |
| import os | |
| from infer import sample_clips, batch_transform_val, videofile_to_frames | |
| from i2v import load_weight, i2v_transform | |
| import numpy as np | |
| import coremltools as ct | |
| def image_to_emb_attn(image, mlmodel): | |
| output_dict = mlmodel.predict({'input': image.resize((224,224))}) | |
| vector = output_dict['vector'] | |
| attention = output_dict['attention'] | |
| attn_cls = attention[0,0,0] | |
| attn_img = attention[0,0,1:].reshape(7,7) | |
| return vector, attn_cls, attn_img | |
| class ScoringWrapper(nn.Module): | |
| def __init__(self): | |
| super(ScoringWrapper, self).__init__() | |
| ckpt_path = os.path.join('..', 'weight', 'ckpt_epoch_59_loss_0.3066582295561343.ckpt') | |
| self.scoring_model = torch.load(ckpt_path, map_location='cpu')['model'].eval() | |
| def forward(self, img_vectors): | |
| img_vectors = img_vectors.view(-1, 512 * self.scoring_model.frames_per_clip) | |
| scores = torch.sigmoid(self.scoring_model(img_vectors)).view(-1) | |
| return scores | |
| class HighlightModel(nn.Module): | |
| def __init__(self, ckpt_path = os.path.join('..', 'weight', 'ckpt_epoch_59_loss_0.3066582295561343.ckpt'), | |
| i2v_path = None, i2v_transform = i2v_transform, batch_transform_val = batch_transform_val): | |
| super().__init__() | |
| if not i2v_path: | |
| i2v_path = os.path.join('..', 'weight', 'heads24_attn_epoch30_loss0.22810565.pt') | |
| self.i2v = load_weight(i2v_path) | |
| self.i2v_transform = i2v_transform | |
| checkpoint = torch.load(ckpt_path, map_location='cpu') | |
| self.scoring_model = checkpoint['model'].eval() | |
| self.batch_transform_val = batch_transform_val | |
| def forward(self, frames): | |
| n_clips = len(frames) - self.scoring_model.frames_per_clip + 1 | |
| x = torch.stack(self.batch_transform_val(frames)) # x.size = (num_frames, 3, 224, 224) | |
| x = self.i2v_transform(x) | |
| img_vectors, attn = self.i2v(x) # img_vectors.size = (num_frames, 512) | |
| # attn.size = (num_frames, 50, 50) | |
| attn = torch.stack([attn[i, 0, 1:].view(7, 7) for i in range(len(attn))]) | |
| x = sample_clips(img_vectors, self.scoring_model.frames_per_clip) | |
| x = x.view(n_clips, -1) | |
| scores = torch.sigmoid(self.scoring_model(x)).view(-1) | |
| return img_vectors, attn, scores | |
| if __name__ == '__main__': | |
| # Load testing video | |
| frames = videofile_to_frames('../get_highlight_example/sports_day_smile.MOV') | |
| # Initialize Pytorch model (i2v + scoring_model) | |
| highlight_model = HighlightModel() | |
| _ = highlight_model.eval() | |
| ''' | |
| Note: | |
| BatchResize is different from PIL.Image resize. | |
| If we purely use BatchResize as preprocessing, the output of pytorch model will be different from coreml | |
| => resize image by PIL.Image.resize() first | |
| ''' | |
| pytorch_img_vector, pytorch_attn, pytorch_scores = highlight_model([frame.resize((224,224)) for frame in frames]) | |
| print("Pytorch scores:", pytorch_scores.detach()) | |
| print("==="*30) | |
| # Load baby clip half precision mlmodel | |
| i2v_mlmodel_filename = os.path.join('..','weight','half_heads24_attn_epoch30_loss0.22810565.pt.mlmodel') | |
| i2v_mlmodel = ct.models.MLModel(i2v_mlmodel_filename) | |
| vectors = [] | |
| attns = [] | |
| for frame in frames: | |
| vector, attn_cls, attn_img = image_to_emb_attn(frame, i2v_mlmodel) | |
| vectors.append(vector.squeeze()) | |
| attns.append(attn_img) | |
| print('MAE of image vectors:', (pytorch_img_vector.detach() - torch.Tensor(vectors)).abs().mean()) | |
| print('MAE of attn:', (pytorch_attn.detach() - torch.Tensor(attns)).abs().mean()) | |
| print("==="*30) | |
| # Load Pytorch scoring model | |
| scoring_model = ScoringWrapper().eval() | |
| scores = [ scoring_model(torch.Tensor(vectors[i:i+3])).detach().item() | |
| for i in range(len(vectors)-scoring_model.scoring_model.frames_per_clip+1)] | |
| print('I2v_mlmodel + pytorch_scoring:', scores) | |
| print('MAE of scores (using i2v_mlmodel):', (pytorch_scores.detach() - torch.Tensor(scores)).abs().mean()) | |
| print("==="*30) | |
| # mlmodel conversion | |
| x = torch.rand(scoring_model.scoring_model.frames_per_clip, 512) | |
| traced = torch.jit.trace(scoring_model, x) | |
| model_input = ct.TensorType(name='input', shape=x.shape) | |
| scoring_mlmodel = ct.convert(source='pytorch', model=traced, inputs=[model_input]) | |
| spec = scoring_mlmodel.get_spec() | |
| scoring_mlmodel_filename = os.path.join('..','weight','score_epoch_59_loss_0.3066.mlmodel') | |
| ct.models.utils.rename_feature(spec, 'var_14', 'score', rename_outputs=True) | |
| ct.models.utils.save_spec(spec, scoring_mlmodel_filename) | |
| scoring_mlmodel = ct.models.MLModel(scoring_mlmodel_filename) | |
| # scoring mlmodel prediction | |
| mlmodel_scores = [ scoring_mlmodel.predict({'input':np.array(vectors[i:i+3])})['score'][0] | |
| for i in range(len(vectors)-scoring_model.scoring_model.frames_per_clip+1)] | |
| print("I2v_mlmodel + scoring_mlmodel:", mlmodel_scores) | |
| print('MAE of scores (using i2v_mlmodel and scoring_mlmodel):', np.abs(pytorch_scores.detach().numpy() - np.array(mlmodel_scores)).mean()) | |
| # assert torch.allclose(pytorch_img_vector.detach(), torch.Tensor(vectors)) | |
| # assert torch.allclose(pytorch_attn.detach(), torch.Tensor(attns)) | |
| # assert np.allclose(pytorch_scores.detach().numpy(), mlmodel_scores) |