Closed GUOhm230 closed 1 week ago
感谢你对我们工作的关注!
从论文中的pipeline可以看出,Emotion-LLaMA在训练过程中冻结了Encoder的参数。因此,在训练代码中,为了节约显存和时间,我们并未将所有Encoder加载到显卡中,而是使用提前提取并保存在本地的特征文件。这些文件包括FaceMAE_feats、VideoMAE_feats和Audio_feats,分别对应论文中的Local Encoder、Temporal Encoder和Audio Encoder的特征。
你可以通过以下Google Drive链接下载我们开源测试集的特征文件以进行复现。如果你需要完整的视频样本,请向MER2023官方申请数据集:
https://drive.google.com/file/d/1DJJ8wP3g4yLT0ZFZ_-H4izJHAGJx2AJ_/view?usp=sharing
https://drive.google.com/file/d/1YyoWabWtAJuFI6ylMM220i_kh_0Nagv9/view?usp=sharing
此外,你还可以通过Hugging Face查看Emotion-LLaMA的online demo代码。输入一个完整的视频后,系统会提取帧和音频,然后提取特征,最后输出结果,这是一个端到端的流程。
你可以参考之前相关的issues,解决你可能遇到的大部分疑问:
https://github.com/ZebangCheng/Emotion-LLaMA/issues/2
https://github.com/ZebangCheng/Emotion-LLaMA/issues/6
如果你对视频处理和特征提取有兴趣,建议你参考MER2023和MER2024的项目仓库,这些都是我入门学习时的重要资料。
最后,如果你成功复现我们的工作,请给予我们正面的反馈,这将是我们的动力。如果遇到任何问题,请随时联系我们。
感谢作者大大这么细心的回答,受宠若惊。我立即尝试一下整个流程。想再次打扰您。如果我想自己用不同的数据集,或者自己去实现Local Encoder、Temporal Encoder和Audio Encoder。根据您的论文。这三个对应的encoder应该分别是:1)Local Encoder https://github.com/sunlicai/MAE-DFER。2)Temporal Encoder::GitHub - MCG-NJU/VideoMAE: [NeurIPS 2022 Spotlight] VideoMAE: Masked Autoencoders are Data-Efficient Learners for Self-Supervised Video Pre-Training。3)Audio Encoder: HuBERT-chinese。是嘛?因为每个encoder中包含不同数据集的不同模型,我无意于做伸手党拿来主义,但是还是想请教您,您是如何选择对应encoder的具体模型的?因为您选择的,必然是效果最好的,所以想请教一下。 再次感谢您的耐心回复,多次打扰,十分抱歉。
Local Encoder用的是在MER2023上预训练的MAE模型。Temporal Encoder用的是在MER2023上预训练过的VideoMAE模型。MAE-DFER(我们没有使用)是在VoxCeleb2上预训练的模型,能提取很好的动态表情特征,对应的是我们工作中的Temporal Encoder。Audio Encoder用的是HuBERT-chinese模型。因为我们工作涉及到的大部分数据集都是中文语言,所以我们选择的Encoder大多都是在中文数据集上预训练过的。
如果你想在不同的数据集上进行训练,建议优先考虑数据分布相似的Encoder,比如与数据集语言一致的Encoder,或者在此类数据集上预训练过的Encoder。此外,你可以继续此尝试增减或者改变不同的Encoder,测试不同的效果,这在我们的代码中是相对容易实现的。
您深夜还能回复我的消息,让我备受鼓舞。我今天先尝试下您那边提取的特征,看看效果。然后再试试用这些Encoder重新得到一下您的那些特征,跑通下这条链路。再试试新的数据。 另外,Global encoder您应该是集成在miniGPT中,是vision transfomers这段代码对吗?那您使用的峰值帧是根据openface得到的AU值再对AU值得频度进行统计得到的对吗? 多次打扰,十分抱歉。并期待您的回复。
再次感谢您的回复。目前我已经根据您的代码,测试了MER-semi里的834个图文视频对,结果: Accuracy: 0.9016786570743405 Precision: 0.8998697764961995 Recall: 0.9016786570743405 F1 Score: 0.8999288544283519 目前我正在打算把Temporal Encoder串起来,用自己的数据去生成encoding。我使用的代码是:
from ipywidgets import Video
from transformers import VideoMAEFeatureExtractor
video_path = "/srv/workspace/g30064845/MLM_project/Emotion-LLaMA/Encoder/Temporal_encoder/VideoMAE/video_dataset/8079.mp4"
Video.from_file(video_path, width=500)
feature_extractor = VideoMAEFeatureExtractor.from_pretrained("/srv/workspace/g30064845/MLM_project/Emotion-LLaMA/Encoder/Temporal_encoder/VideoMAE/model/videomae-base-finetuned-kinetics/")
print("model init done!")
from decord import VideoReader, cpu
import numpy as np
vr = VideoReader(video_path, num_threads=1, ctx=cpu(0))
def sample_frame_indices(clip_len, frame_sample_rate, seg_len):
converted_len = int(clip_len * frame_sample_rate)
end_idx = np.random.randint(converted_len, seg_len)
str_idx = end_idx - converted_len
index = np.linspace(str_idx, end_idx, num=clip_len)
index = np.clip(index, str_idx, end_idx - 1).astype(np.int64)
return index
vr.seek(0)
index = sample_frame_indices(clip_len=16, frame_sample_rate=4, seg_len=len(vr))
buffer = vr.get_batch(index).asnumpy()
print(buffer.shape)
video = [buffer[i] for i in range(buffer.shape[0])]
encoding = feature_extractor(video, return_tensors="pt")
print(encoding.pixel_values.shape) # [1, 16, 3, 224, 224])
但是最后提取出来的结果是:torch.Size([1, 16, 3, 224, 224])。但是我看数据形式都是[1024,]请问您那边具体是如何操作的呢?是我哪里还没有理解到嘛?请您赐教。 十分期待您的回复。多次打扰,深感抱歉,。
感谢您不厌其烦的回复,我深受鼓舞。昨天的提问体现我对该领域的不熟悉,刚入门大模型,请您多包涵。 我一开始以为feature_extractor(video, return_tensors="pt")是特征提取,正符合videoMAE要做的事。所以认为结果就是encoding。经过查验发现,encoding只有pixel_values这一个键值,16应该就是论文中您所说的人脸对齐的帧。那么encoding应该是模型的输入。我接下来进行如下操作:
from transformers import VideoMAEFeatureExtractor, VideoMAEForVideoClassification
import torch
device = "cuda:7"
model = VideoMAEForVideoClassification.from_pretrained(model_path)
model.to(device)
print("model:", model)
pixel_values = encoding.pixel_values.to(device)
# forward pass
with torch.no_grad():
outputs = model(pixel_values)
logits = outputs.logits
print("outputs:", outputs)
print("logits:", logits)
结果为:
ImageClassifierOutput(loss=None, logits=tensor([[-0.5093, 0.0678]], device='cuda:7'), hidden_states=None, attentions=None) tensor([[-0.5093, 0.0678]], device='cuda:7')
看起来这是一个分类的结果。但是,分类前的模型输出是:
(output): VideoMAEOutput(
(dense): Linear(in_features=4096, out_features=1024, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
(layernorm_before): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)
(layernorm_after): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)
)
)
)
(layernorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)
)
(classifier): Linear(in_features=1024, out_features=2, bias=True)
)
十分冒昧再次请教您。您在做videoMAE时得到的结果就是用的VideoMAEoutput的结果,也就是classifier之前的值吗?如果不是,您能否告知您的具体操作呢?
不用客气。 我们在使用VideoMAE提取特征时,保留的是(classifier): Linear分类前一层的特征。
def forward(self, x):
x = self.forward_features(x)
# x = self.head(self.fc_dropout(x))
return x
如上述代码所示,我们注释了self.head()函数,它的作用和你代码中的(classifier)一致,是一个分类的线性层,如下:
self.head = nn.Linear(embed_dim, num_classes)
感谢感谢,那我这样提取是对的。这样的话,我的Temporal encoder就做对了。
Local Encoder用的是在_MER2023_上预训练的MAE模型。Temporal Encoder用的是在_MER2023_上预训练过的VideoMAE模型。MAE-DFER(我们没有使用)是在_VoxCeleb2_上预训练的模型,能提取很好的动态表情特征,对应的是我们工作中的Temporal Encoder。Audio Encoder用的是HuBERT-chinese模型。因为我们工作涉及到的大部分数据集都是中文语言,所以我们选择的Encoder大多都是在中文数据集上预训练过的。
如果你想在不同的数据集上进行训练,建议优先考虑数据分布相似的Encoder,比如与数据集语言一致的Encoder,或者在此类数据集上预训练过的Encoder。此外,你可以继续此尝试增减或者改变不同的Encoder,测试不同的效果,这在我们的代码中是相对容易实现的。
多次打扰深感抱歉,并十分感谢您的回复。 我目前尝试使用您给出的项目:https://github.com/facebookresearch/mae 我根据其中的demo/mae_visualize.ipynb中给出的模型:mae_vit_large_patch16。加载发现模型结构为:
MaskedAutoencoderViT( (patch_embed): PatchEmbed( (proj): Conv2d(3, 1024, kernel_size=(16, 16), stride=(16, 16)) (norm): Identity() ) (blocks): ModuleList( (0-23): 24 x Block( (norm1): LayerNorm((1024,), eps=1e-06, elementwise_affine=True) (attn): Attention( (qkv): Linear(in_features=1024, out_features=3072, bias=True) (attn_drop): Dropout(p=0.0, inplace=False) (proj): Linear(in_features=1024, out_features=1024, bias=True) (proj_drop): Dropout(p=0.0, inplace=False) ) (ls1): Identity() (drop_path1): Identity() (norm2): LayerNorm((1024,), eps=1e-06, elementwise_affine=True) (mlp): Mlp( (fc1): Linear(in_features=1024, out_features=4096, bias=True) (act): GELU(approximate='none') (drop1): Dropout(p=0.0, inplace=False) (fc2): Linear(in_features=4096, out_features=1024, bias=True) (drop2): Dropout(p=0.0, inplace=False) ) (ls2): Identity() (drop_path2): Identity() ) ) (norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True) (decoder_embed): Linear(in_features=1024, out_features=512, bias=True) (decoder_blocks): ModuleList( (0-7): 8 x Block( (norm1): LayerNorm((512,), eps=1e-06, elementwise_affine=True) (attn): Attention( (qkv): Linear(in_features=512, out_features=1536, bias=True) (attn_drop): Dropout(p=0.0, inplace=False) (proj): Linear(in_features=512, out_features=512, bias=True) (proj_drop): Dropout(p=0.0, inplace=False) ) (ls1): Identity() (drop_path1): Identity() (norm2): LayerNorm((512,), eps=1e-06, elementwise_affine=True) (mlp): Mlp( (fc1): Linear(in_features=512, out_features=2048, bias=True) (act): GELU(approximate='none') (drop1): Dropout(p=0.0, inplace=False) (fc2): Linear(in_features=2048, out_features=512, bias=True) (drop2): Dropout(p=0.0, inplace=False) ) (ls2): Identity() (drop_path2): Identity() ) ) (decoder_norm): LayerNorm((512,), eps=1e-06, elementwise_affine=True) (decoder_pred): Linear(in_features=512, out_features=768, bias=True) ) 请问:
- 您是否使用的是相同的模型架构?然后用MER2023做微调?
- 请问您最终提取的1024维特征是选择decoder_embed层之前的输出?也就是层归一化后的输出结果: (norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True)?
- 该模型的输入是单张图片,有16张图片,您是cat后把模型输入变成和Video_mae一样的【1,16, 224, 224】嘛?还是有别的处理方法呢? 十分抱歉再次打扰。您的工作我十分感兴趣,也想以此为入口,学习到更多多模态大模型的方法和原理。 期待您的回复。祝一切顺利
models_vit.py
,并进行了之前回复中的相关注释。十分感谢您的用心回复,让我在复现您这一非常有建设性工作时事半功倍。深夜打扰,十分抱歉。
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True) (fc_norm): Identity() (head_drop): Dropout(p=0.0, inplace=False) (head): Linear(in_features=1024, out_features=1000, bias=True) )
正如您前面所说的,您最终的输出是取到fc_norm之前,norm后的输出对吗? 多有冒昧打扰,十分抱歉。我们导师也对您的工作十分感兴趣,鼓励我继续做下去。 十分期待您的回复。 祝好,周末愉快!
1.在MAE模型的仓库中有预训练后的模型链接:
https://dl.fbaipublicfiles.com/mae/pretrain/mae_pretrain_vit_large.pth
2.可以这样理解,最终的输出是取到fc_norm之前,norm后的输出。
十分感谢您的回复,周末打扰,多有冒昧,十分抱歉。
关于hubert encoder,我想向您确认下:
关于hubert encoder,我想向您确认下:
- 我使用了chinese-hubert-large模型,该模型的直接输出应该就是我们需要提取的特征对吗?
嗯嗯,模型的输出应该是和音频的长度相关:N * 1024。你可以直接取class token作为音频特征,也可以对输出求平均。具体操作可以参考feature_extraction
作者您好,关于Local encoder,我做了以下操作: 代码:
class VisionTransformer(timm.models.vision_transformer.VisionTransformer):
"""
Vision Transformer with support for global average pooling
"""
def __init__(self, global_pool=False, **kwargs):
super(VisionTransformer, self).__init__(**kwargs)
# del self.norm
del self.fc_norm
del self.head
self.global_pool = global_pool
if self.global_pool:
norm_layer = kwargs['norm_layer']
embed_dim = kwargs['embed_dim']
self.fc_norm = norm_layer(embed_dim)
del self.norm # remove the original norm
def forward_features(self, x):
B = x.shape[0]
x = self.patch_embed(x)
cls_tokens = self.cls_token.expand(B, -1, -1) # stole cls_tokens impl from Phil Wang, thanks
x = torch.cat((cls_tokens, x), dim=1)
x = x + self.pos_embed
x = self.pos_drop(x)
for blk in self.blocks:
x = blk(x)
if self.global_pool:
x = x[:, 1:, :].mean(dim=1) # global pool without cls token
outcome = self.fc_norm(x)
else:
x = self.norm(x)
outcome = x[:, 0]
return outcome
def forward(self, input):
return self.forward_features(input)
if __name__ == "__main__":
img_path = "./Emotion-LLaMA/Encoder/Local_encoder/mae/images/5-220512103500.jpg"
model_path = "./Emotion-LLaMA/Encoder/Local_encoder/mae/model/mae_pretrain_vit_large.pth"
transform = transforms.Compose([
transforms.Resize((256, 256)),
transforms.CenterCrop(224),
transforms.ToTensor(), # 将PIL图像转换为Tensor
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), # 标准化
])
img = Image.open(img_path).convert('RGB')
tenfor_image = transform(img).unsqueeze(0).cuda(7)
print(tenfor_image.shape)
model = vit_large_patch16()
checkpoint = torch.load(model_path)
model.load_state_dict(checkpoint["model"])
print(model.cuda(7))
for key, value in model.state_dict().items():
print(key, value.device)
# 现在要看看图片输进来以怎样的方式
h, w = 256, 256
input = torch.arange(3*h*w, dtype=torch.float32).reshape(3, h, w).unsqueeze(0).cuda(7)
output = model(tenfor_image)
print(output.shape)
如上,我在init中删除了self.fc_norm以及self.head 然后增加self.forward()以直接运行forward_features().但是我发现本处的forward_features()与timm.models.vision_transformer.VisionTransformer中的forward_features()不一样,前者是x = self.pos_drop(x) 后者是x = self.norm_pre(x)。 请问:
1.我用的是MAE Github仓库的代码,可能和timm库中代码不一样。
https://github.com/facebookresearch/mae 2.之前推荐的MER2023的Github仓库中有完整的数据处理流程,包括音频提取,文本提取,人脸提取与对齐等具体的实现代码。 https://github.com/CASIA-Affective-Computing-Group/MER2023-Baseline
您好,最近在学一些基础的东西,好久没搞这个代码了。今天把encoder集成到emotion-llama中了,调试了一下效果,目前有一些问题:
1.抱歉,我们的工作没有接触到双通道音频,没有考虑过如何处理这种情况。可能最简单的方式的就是两个通道做平均。 2.DFEW dataset有划分训练集和测试集,你可以尝试先再训练集上instruction tuning,然后再测试。 3.这个问题让我感到困惑,我们的工作是使用Lora调优后的模型进行测试。 4.checkpoint_best.pth保存的是Lora新学习的参数。llama的参数加上lora的参数就是instruction tuning后完整的参数。
您好,请问:
再次抱歉,多次打扰。祝您身体健康,万事如意。 十分期待您的回复。
1.text为中文字符对模型是有影响的,因为我们使用的LLaMA-2模型只支持英语。 2.我们在之前的工作中预训练了MAE和VideoMAE,所以在这个工作中冻结了Encoder。 3.我们也做过相同的工作,在Decoder的hidden state后面加上线性分类层或者预测层(Linear层)然后进行分类,发现效果一般。于是我们让大模型做它最擅长的事情,直接输出情绪标签的token,再解码为字符。 4.在MER2023上Instruction tuning后,Emotion-LLaMA表现出了强大的情绪理解能力。在DFEW上的zero-shot分数高于主流的多模态大模型,但和传统的supervised method方法比还有差距。
看了您提供的代码,您在模型输入之前本来需要对video进行编码的,不过您那是直接读取文件获取:FaceMAE_feats, VideoMAE_feats, Audio_feats。请问这些数据您是怎么获取的?对应论文的哪一步呢?有开源的数据嘛? 我是从事该方向的研一新生,十分期待您的回复。