Download visual_compressor.py from Prabhu3674/DocOwl2: direct link, hf CLI and curl.
- Browser
- Download file 20.4 kB
-
https://huggingface.co/Prabhu3674/DocOwl2/resolve/main/visual_compressor.py
- Command line
-
hf download hf://Prabhu3674/DocOwl2/visual_compressor.py
-
curl -L -o visual_compressor.py https://huggingface.co/Prabhu3674/DocOwl2/resolve/main/visual_compressor.py
20.4 kB
| 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 |