DocOwl2 / visual_compressor.py
AnwenHu's picture
Upload 13 files
fc7239c verified
import math
from typing import Any, Optional, Tuple, Union
from transformers.modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, BaseModelOutputWithPastAndCrossAttentions
from transformers.modeling_utils import PreTrainedModel
from transformers.pytorch_utils import find_pruneable_heads_and_indices, prune_linear_layer
import numpy as np
import torch
import torch.nn as nn
import torch.utils.checkpoint
from icecream import ic
from flash_attn.flash_attn_interface import flash_attn_varlen_func as flash_attn_unpadded_func
from einops import rearrange
class MplugDocOwlVisualMLP(nn.Module):
def __init__(self, config):
super().__init__()
self.config = config
in_features = config.high_reso_cross_hid_size
self.act = nn.SiLU()
ffn_hidden_size = int(2 * 4 * in_features / 3)
multiple_of = 256
ffn_hidden_size = multiple_of * ((ffn_hidden_size + multiple_of - 1) // multiple_of)
self.w1 = nn.Linear(in_features, ffn_hidden_size)
self.w2 = nn.Linear(ffn_hidden_size, in_features)
self.w3 = nn.Linear(in_features, ffn_hidden_size)
self.ffn_ln = nn.LayerNorm(ffn_hidden_size, eps=config.layer_norm_eps)
torch.nn.init.zeros_(self.w1.bias.data)
torch.nn.init.zeros_(self.w2.bias.data)
torch.nn.init.zeros_(self.w3.bias.data)
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
hidden_states = self.act(self.w1(hidden_states)) * self.w3(hidden_states)
hidden_states = self.ffn_ln(hidden_states)
hidden_states = self.w2(hidden_states)
return hidden_states
class FlashCrossAttention(torch.nn.Module):
"""Implement the scaled dot product attention with softmax.
Arguments
---------
softmax_scale: The temperature to use for the softmax attention.
(default: 1/sqrt(d_keys) where d_keys is computed at
runtime)
attention_dropout: The dropout rate to apply to the attention
(default: 0.0)
"""
def __init__(self, causal=False, softmax_scale=None, attention_dropout=0.0,
device=None, dtype=None):
super().__init__()
self.softmax_scale = softmax_scale
self.dropout_p = attention_dropout
def forward(self, q, k, v, **kwargs):
"""Implements the multihead softmax attention.
Arguments
---------
q, k, v: The tensor containing the query, key, and value. (B, S, H, D)
or
q: (Sum_q, H, D), k,v : (Sum_k, H, D),
must with batch_size, max_seqlen_q, max_seqlen_k, cu_seqlens_q, cu_seqlens_k in kwargs
"""
assert all((i.dtype in [torch.float16, torch.bfloat16] for i in (q,k,v)))
assert all((i.is_cuda for i in (q,k,v)))
if q.dim() == 4:
batch_size, seqlen_q = q.shape[0], q.shape[1]
q = rearrange(q, 'b s ... -> (b s) ...')
cu_seqlens_q = torch.arange(0, (batch_size + 1) * seqlen_q, step=seqlen_q, dtype=torch.int32,
device=q.device)
else:
batch_size, seqlen_q = kwargs['batch_size'], kwargs['max_seqlen_q']
cu_seqlens_q = kwargs['cu_seqlens_q']
if k.dim() == 4:
seqlen_k = k.shape[1]
k, v = [rearrange(x, 'b s ... -> (b s) ...') for x in [k, v]]
cu_seqlens_k = torch.arange(0, (batch_size + 1) * seqlen_k, step=seqlen_k, dtype=torch.int32,
device=q.device)
else:
seqlen_k = kwargs['max_seqlen_k']
cu_seqlens_k = kwargs['cu_seqlens_k']
# q, k, v = [rearrange(x, 'b s ... -> (b s) ...') for x in [q, k, v]]
# self.dropout_p = 0
"""print('FlashCrossAttention: q.shape:', q.shape)
print('FlashCrossAttention: k.shape:', k.shape)
print('FlashCrossAttention: v.shape:', v.shape)
print('FlashCrossAttention: cu_seqlens_q:', cu_seqlens_q)
print('FlashCrossAttention: cu_seqlens_k:', cu_seqlens_k)"""
# print('visual_compressor.py q.shape:', q.shape, ' k.shape:', k.shape, ' v.shape:', v.shape)
output = flash_attn_unpadded_func(
q, k, v, cu_seqlens_q, cu_seqlens_k, seqlen_q, seqlen_k,
self.dropout_p if self.training else 0.0,
softmax_scale=self.softmax_scale, causal=False
)
if q.dim() == 4: # keep the shape of output shape same as the input query
output = rearrange(output, '(b s) ... -> b s ...', b=batch_size)
return output
class MplugDocOwlVisualMultiHeadAttention(nn.Module):
def __init__(self, config):
super().__init__()
self.config = config
if config.high_reso_cross_hid_size % config.high_reso_cross_num_att_heads != 0:
raise ValueError(
"The hidden size (%d) is not a multiple of the number of attention heads (%d)"
% (config.high_reso_cross_hid_size, config.high_reso_cross_num_att_heads)
)
if config.high_reso_cross_hid_size // config.high_reso_cross_num_att_heads > 256:
raise ValueError(
"The hidden size of each head (%d) > 256 and is illegal for flash attention"
% (config.high_reso_cross_hid_size // config.high_reso_cross_num_att_heads)
)
self.num_attention_heads = config.high_reso_cross_num_att_heads
self.attention_head_size = int(config.high_reso_cross_hid_size / config.high_reso_cross_num_att_heads)
self.all_head_size = self.num_attention_heads * self.attention_head_size
self.query = nn.Linear(config.high_reso_cross_hid_size, self.all_head_size)
self.key = nn.Linear(config.high_reso_cross_hid_size, self.all_head_size)
self.value = nn.Linear(config.high_reso_cross_hid_size, self.all_head_size)
self.core_attention_flash = FlashCrossAttention(attention_dropout=config.high_reso_cross_dropout)
# bias init
torch.nn.init.zeros_(self.query.bias.data)
torch.nn.init.zeros_(self.key.bias.data)
torch.nn.init.zeros_(self.value.bias.data)
def transpose_for_scores(self, x):
# [B, S, D] -> [B, S, H, D] or [Sum_S, D] -> [Sum_S, H, D]
new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size)
x = x.view(*new_x_shape)
return x
def forward(
self,
hidden_states,
encoder_hidden_states=None,
**kwargs
):
# assert not torch.isnan(hidden_states).any()
# assert not torch.isnan(encoder_hidden_states).any()
key = self.transpose_for_scores(self.key(encoder_hidden_states))
value = self.transpose_for_scores(self.value(encoder_hidden_states))
query = self.transpose_for_scores(self.query(hidden_states))
# print('visual_compressor.py key(after projection): ', key.shape, key)
# print('visual_compressor.py value(after projection): ', value.shape, value)
# print('visual_compressor.py query(after projection): ', query.shape, query)
# assert not torch.isnan(key).any()
# assert not torch.isnan(value).any()
# assert not torch.isnan(query).any()
outputs = self.core_attention_flash(q=query, k=key, v=value, **kwargs)
outputs = rearrange(outputs, 's h d -> s (h d)').contiguous()
# print('visual_compressor.py outputs(after cross_att): ', outputs.shape, outputs)
return outputs
class MplugDocOwlVisualCrossOutput(nn.Module):
def __init__(self, config):
super().__init__()
dim = config.high_reso_cross_hid_size
self.out_proj = nn.Linear(dim, dim, bias=True)
self.norm2 = nn.LayerNorm(dim)
self.mlp = MplugDocOwlVisualMLP(config)
# bias init
torch.nn.init.zeros_(self.out_proj.bias.data)
def forward(self, hidden_states: torch.Tensor, input_tensor: torch.Tensor) -> torch.Tensor:
input_tensor = input_tensor + self.out_proj(hidden_states)
input_tensor = input_tensor + self.mlp(self.norm2(input_tensor))
return input_tensor
class MplugDocOwlVisualCrossAttentionLayer(nn.Module):
def __init__(self, config):
super().__init__()
self.attention = MplugDocOwlVisualMultiHeadAttention(config)
self.output = MplugDocOwlVisualCrossOutput(config)
self.norm1 = nn.LayerNorm(config.high_reso_cross_hid_size)
self.normk = nn.LayerNorm(config.high_reso_cross_hid_size)
def forward(
self,
hidden_states: torch.Tensor,
encoder_hidden_states: Optional[torch.FloatTensor] = None,
**kwargs
) -> Tuple[torch.Tensor]:
# print('visual_compressor.py hidden_states: ', hidden_states.shape, hidden_states)
# print('visual_compressor.py encoder_hidden_states: ', encoder_hidden_states.shape, encoder_hidden_states)
# assert not torch.isnan(hidden_states).any()
# assert not torch.isnan(encoder_hidden_states).any()
hidden_states = self.norm1(hidden_states)
encoder_hidden_states = self.normk(encoder_hidden_states)
# print('visual_compressor.py hidden_states(after norm): ', hidden_states.shape, hidden_states)
# print('visual_compressor.py encoder_hidden_states(after norm): ', encoder_hidden_states.shape, encoder_hidden_states)
attention_output = self.attention(
hidden_states,
encoder_hidden_states,
**kwargs
)
outputs = self.output(attention_output, hidden_states)
return outputs
class MplugDocOwlVisualCrossAttentionEncoder(nn.Module):
def __init__(self, config):
super().__init__()
self.config = config
self.layer_num = config.layer
self.layers = nn.ModuleList(
[MplugDocOwlVisualCrossAttentionLayer(config) for layer_idx in range(self.layer_num)]
)
self.gradient_checkpointing = True
def forward(
self,
hidden_states: torch.Tensor,
encoder_hidden_states: Optional[torch.FloatTensor] = None,
**kwargs
):
for i in range(self.layer_num):
layer_module = self.layers[i]
layer_outputs = layer_module(
hidden_states,
encoder_hidden_states,
**kwargs
)
hidden_states = layer_outputs
return hidden_states
def ensemble_crop_feats(crop_feats, patch_positions, col_feat_num):
"""
ensemble vision feats from different crops to a feature map according the position of the raw image
crop_feats: [N_crop, Len_feat, D]
patch_positions: [N_crop, 2], 2 == (rowl_index, col_index)
col_feat_num: the feature num of a row in a crop image
"""
assert crop_feats.size(0) == patch_positions.size(0)
row_feats = []
crop_row = torch.max(patch_positions[:,0])+1 #
crop_feats = rearrange(crop_feats, '(R C) L D -> R C L D', R=crop_row) # [N_crop_row, N_crop_col, Len_feat, D]
crop_feats = rearrange(crop_feats, 'R C (X Y) D-> R C X Y D', Y=col_feat_num) # [N_crop_row, N_crop_col, Len_row_feat, Len_col_feat, D]
# 1. concatenate same row feats across crops; 2. ensemble row feats to get 1 feature map
hw_feats = rearrange(crop_feats, 'R C X Y D-> (R X) (C Y) D') # [N_crop_row x Len_row_feat, N_crop_col x Len_col_feat, D]
return hw_feats
def group_window_feats(feats, window):
"""
collect vision feats from a window (win_row, win_col) to 1 group
feats: [H, W, D]
window: (win_row, win_col)
return: [H/win_row, H/win_col, win_row x win_col, D]
"""
group_feats = rearrange(feats, '(X R) (Y C) D -> (X Y) (R C) D', R=window[0], C=window[1]) # [H/win_row x H/win_col, win_row x win_col, D]
return group_feats
def distinguish_global_crop_features(hidden_states, patch_positions, reorganize_crop_feats=True, col_feat_num=None, group_feats_by_crop_shape=False, keep_row_col=False):
"""
distinguish global and crop features with the help of patcg_positions
# hidden_states: [B, s+1, h]
# (B is the sum of cropped num across samples in a micro_batch, s is the visual tokens, +1 means the vit end token)
# patch_positions: [B, 2],
# 2 == (rowl_index, col_index), the first crop is (0,0), global img is (anchor_max, anchor_max)
col_feat_num is used when reorganize_crop_feats == True
outputs:
img_global_features: list of [Len_global_feat, D]
img_crop_features: list of [Len_global_feat, D]
"""
hidden_states = hidden_states[:, :-1, :] # remove the last vit end token emb
# the first crop is (0,0)
first_crop_indices = (patch_positions.sum(dim=-1) == 0).nonzero().squeeze(1) # Num_img
# the global image is before the first crop
global_indices = first_crop_indices - 1 # Num_img
# print('vision2text_model.py patch_positions:', patch_positions)
# print('vision2text_model.py global_indices:', global_indices)
# collect cropped vision features of an identical image
batch_size = hidden_states.size(0)
img_global_features = []
img_crop_features = [] # store list of Num_crop (variable) x Len_feat (fixed)
img_crop_positions = [] # store list of Num_crop (variable) x 2
for i in range(len(global_indices)):
index = global_indices[i]
img_global_features.append(hidden_states[index])
if i == (len(global_indices)-1):
img_crop_features.append(hidden_states[index+1:])
img_crop_positions.append(patch_positions[index+1:])
else:
next_index = global_indices[i+1]
img_crop_features.append(hidden_states[index+1:next_index])
img_crop_positions.append(patch_positions[index+1:next_index])
if reorganize_crop_feats:
for i in range(len(img_crop_features)):
img_crop_features[i] = ensemble_crop_feats(img_crop_features[i], img_crop_positions[i], col_feat_num) # [H W D]
if group_feats_by_crop_shape: # collect vision feats from a window (crop_row, crop_col) to 1 group
crop_row = torch.max(img_crop_positions[i][:,0])+1 #
crop_col = torch.max(img_crop_positions[i][:,1])+1 #
img_crop_features[i] = group_window_feats(img_crop_features[i], window=(crop_row, crop_col)) # [H/crop_row x W/crop_col, crop_row x crop_row, D]
else:
# img_crop_features = [rearrange(x, 'H W D -> (H W) D') for x in img_crop_features]
if not keep_row_col:
img_crop_featuress[i] = rearrange(img_crop_featuress[i], 'H W D -> (H W) D')
else:
img_crop_features = [rearrange(x, 'N L D -> (N L) D') for x in img_crop_features]
return img_global_features, img_crop_features
class MplugDocOwlHRDocCompressor(PreTrainedModel):
"""
After vision-to-text module, use low-resolution global features to select high-resolution crop features with cross-attention
the key/value from high-resolution crop features are contrained in a window size
positions of the features within the window in raw images are the same as the global query features
"""
def __init__(self, config, output_hidden_size, v2t_img_col_tokens):
super().__init__(config)
self.use_flash_attn = True
assert self.use_flash_attn
self.v2t_img_col_tokens = v2t_img_col_tokens
self.compressor_crossatt = MplugDocOwlVisualCrossAttentionEncoder(config)
self.compressor_fc = torch.nn.Linear(output_hidden_size, output_hidden_size)
self.compressor_eos = torch.nn.Parameter(torch.randn(1, 1, output_hidden_size))
def forward(self, hidden_states, patch_positions=None):
# hidden_states: outputs of vision2textmodel: [Sum(crop), s+1, h]
# (Sum(crop) is the sum of cropped num across samples in a micro_batch, s is the visual tokens, +1 is the special vit_eos token added in H-Reducer)
# patch_positions: [Sum(crop), 2]
# print('visual_compressor.py HRDocCompressor hidden_states.shape:', hidden_states.shape)
# print('visual_compressor.py HRDocCompressor patch_positions.shape:', patch_positions.shape)
# N_img x [L_global (fixed), D], N_img x [L_global (fixed), Crop_row x Crop_Col (Variable), D]
img_global_features, img_crop_features = distinguish_global_crop_features(hidden_states,
patch_positions,
reorganize_crop_feats=True,
col_feat_num=self.v2t_img_col_tokens,
group_feats_by_crop_shape=True)
# cross-attention to accumulate high-resolution features
# if self.use_flash_attn: # flash_attn_varlen_func don't need to pad crop_features
img_global_features = torch.stack(img_global_features, dim=0).to(hidden_states.device) # Num_img x Len_global_feat x D
batch_size, global_feat_num, seqlen_q = img_global_features.shape[0], img_global_features.shape[1], 1
img_global_features = rearrange(img_global_features, 'b s ... -> (b s) ...')
cu_seqlens_q = torch.arange(0, batch_size*global_feat_num+1, step=1, dtype=torch.int32, device=img_global_features.device) # # (Num_img x Len_global_feat +1, )
cu_seqlens_k = [0]
max_seqlens_k = 0
for crop_feat in img_crop_features:
for i in range(crop_feat.shape[0]):
cu_seqlens_k.append(cu_seqlens_k[-1]+crop_feat.shape[1]) # same k within a image shares the seq len
max_seqlens_k = max(max_seqlens_k, crop_feat.size(1))
cu_seqlens_k = torch.tensor(cu_seqlens_k, dtype=torch.int32).to(hidden_states.device) # (Num_img x Len_global_feat+1, )
# cu_seqlens_k = torch.arange(0, (batch_size + 1) * max_seqlens_k, step=max_seqlens_k, dtype=torch.int32, device=img_global_features.device) # # (Num_img+1, )
img_crop_features = torch.cat([rearrange(x, 'N L D -> (N L) D') for x in img_crop_features], dim=0).to(hidden_states.device) # Sum(L_hr) x D
flash_kwargs = {
'batch_size': batch_size*global_feat_num, # each feat in global feats use different keys
'max_seqlen_q': seqlen_q, # key are unique for each query
'max_seqlen_k': max_seqlens_k,
'cu_seqlens_q': cu_seqlens_q, # the seq len of each q
'cu_seqlens_k': cu_seqlens_k # the seq len of each k
}
# print('visual_compressor.py HRDocCompressor img_global_features.shape:', img_global_features.shape, img_global_features)
# print('visual_compressor.py HRDocCompressor img_crop_features.shape:', img_crop_features.shape, img_crop_features)
"""print('visual_compressor.py HRDocCompressor cu_seqlens_q, cu_seqlens_q.shape:', cu_seqlens_q, cu_seqlens_q.shape)
print('visual_compressor.py HRDocCompressor cu_seqlens_k, cu_seqlens_k.shape:', cu_seqlens_k, cu_seqlens_k.shape)"""
# assert not torch.isnan(img_global_features).any()
# assert not torch.isnan(img_crop_features).any()
for x_name, x in self.compressor_crossatt.named_parameters():
try:
assert not torch.isnan(x).any()
# print('visual_compressor.py ', x_name, x.shape, x)
except Exception as e:
print(e)
print('visual_compressor.py nan', x_name, x.shape, x)
hidden_states = self.compressor_crossatt(
img_global_features.contiguous(), # Sum(L_global) x D
img_crop_features.contiguous(), # Sum(L_hr) x D
**flash_kwargs
) # Sum(L_global) x D
hidden_states = rearrange(hidden_states, '(B S) D -> S B D', B=batch_size) # L_global x N_img x D
hidden_states = self.compressor_fc(hidden_states) # L_global x N_img x D
hidden_states = hidden_states.transpose(0, 1).contiguous() # N_img x L_global x D
# print('visual_compressor.py hidden_states:', hidden_states.shape)
hidden_states = torch.cat([hidden_states, self.compressor_eos.repeat(hidden_states.shape[0], 1, 1)], dim=1) # N_img x (L_global+1) x D
# print('visual_compressor.py HRDocCompressor hidden_states.shape:', hidden_states.shape)
return hidden_states