U
    T?hTY                     @   s  d dl Z d dlZd dlZd dlmZ d dlmZmZmZ d dl	Z
d dlmZmZmZ d dlmZ eeeee
jdddZeeee
jd	d
dZeedddZd,eeeeeee
jdddZeeee
jf dddZeeeeeeeeeeeedddZeeeeeeeeeeedddZdd Zd-eee ee ee eee
j ee
j ee
j f ddd Zd.eee ee ee eee
j ee
j ee
j f d!d"d#Zd$d% Zeeeeeeeee ee ee eeeed&d'd(Z d)d* Z!e"d+kre!  dS )/    N)Path)DictOptionalTuple)
ModelProtoTensorProtonumpy_helper)	OnnxModel)	input_ids
batch_sizesequence_lengthdictionary_sizereturnc                 C   sp   | j jjtjtjtjfksttj	j
|||ftjd}| j jjtjkrRt|}n| j jjtjkrlt|}|S )a`  Create input tensor based on the graph input of input_ids

    Args:
        input_ids (TensorProto): graph input of the input_ids input tensor
        batch_size (int): batch size
        sequence_length (int): sequence length
        dictionary_size (int): vocabulary size of dictionary

    Returns:
        np.ndarray: the input tensor created
    )sizedtype)typetensor_type	elem_typer   FLOATINT32INT64AssertionErrornprandomrandintint32float32int64)r
   r   r   r   data r   Y/var/www/html/venv/lib/python3.8/site-packages/onnxruntime/transformers/bert_test_data.pyfake_input_ids_data   s    

r!   )segment_idsr   r   r   c                 C   sl   | j jjtjtjtjfksttj	||ftj
d}| j jjtjkrNt|}n| j jjtjkrht|}|S )a,  Create input tensor based on the graph input of segment_ids

    Args:
        segment_ids (TensorProto): graph input of the token_type_ids input tensor
        batch_size (int): batch size
        sequence_length (int): sequence length

    Returns:
        np.ndarray: the input tensor created
    r   )r   r   r   r   r   r   r   r   r   zerosr   r   r   )r"   r   r   r   r   r   r    fake_segment_ids_data2   s    

r%   Zmax_sequence_lengthaverage_sequence_lengthc                 C   sL   |dkr|| kst d| | kr4td| |  | S tdd| d S d S )N      )r   r   r   r&   r   r   r    get_random_lengthM   s    r*   r)   )
input_maskr   r   r'   random_sequence_length	mask_typer   c                 C   s\  | j jjtjtjtjfkst|dkrrtj	|tj
d}|rXt|D ]}t||||< qBnt|D ]}|||< q`n|dkrtj||ftj
d}|rt|D ](}t||}t|D ]}	d|||	f< qqn4tj	||ftj
d}
|
|d|
jd d|
jd f< n |dksttj|d d tj
d}|rt|D ]}t||||< q2t|d D ]r}|dkr~||| d  ||d   nd||| < |dkr||| d  ||d   nd|d| d | < qTnTt|D ]}|||< qt|d D ].}|| ||| < || |d| d | < q| j jjtjkr<t|}n| j jjtjkrXt|}|S )a"  Create input tensor based on the graph input of segment_ids.

    Args:
        input_mask (TensorProto): graph input of the attention mask input tensor
        batch_size (int): batch size
        sequence_length (int): sequence length
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type (int): mask type - 1: mask index (sequence length excluding paddings). Shape is (batch_size).
                                     2: 2D attention mask. Shape is (batch_size, sequence_length).
                                     3: key len, cumulated lengths of query and key. Shape is (3 * batch_size + 2).

    Returns:
        np.ndarray: the input tensor created
    r(   r#   r)   Nr      )r   r   r   r   r   r   r   r   r   Zonesr   ranger*   r$   shaper   r   )r+   r   r   r'   r,   r-   r   iZactual_seq_lenjtempr   r   r    fake_input_mask_dataW   sN    

$2@
r4   )	directoryinputsc              
   C   s   t j| sRzt |  W n$ tk
r>   td|  d Y qbX td|  d ntd|  d t| D ]N\}\}}t	||}t
t j| d| dd	}||  W 5 Q R X qnd
S )zOutput input tensors of test data to a directory

    Args:
        directory (str): path of a directory
        inputs (Dict[str, np.ndarray]): map from input name to value
    zCreation of the directory z failedz#Successfully created the directory  zWarning: directory z$ existed. Files will be overwritten.Zinput_.pbwbN)ospathexistsmkdirOSErrorprint	enumerateitemsr   
from_arrayopenjoinwriteSerializeToString)r5   r6   indexnamer   Ztensorfiler   r   r    output_test_data   s    rJ   )r   r   
test_casesr   verboserandom_seedr
   r"   r+   r'   r,   r-   c                 C   s   |dk	st tj| t| g }t|D ]r}t|| ||}|j|i}|r`t|| |||j< |r|t|| ||	|
|||j< |rt	|dkrt
d| || q.|S )a  Create given number of input data for testing

    Args:
        batch_size (int): batch size
        sequence_length (int): sequence length
        test_cases (int): number of test cases
        dictionary_size (int): vocabulary size of dictionary for input_ids
        verbose (bool): print more information or not
        random_seed (int): random seed
        input_ids (TensorProto): graph input of input IDs
        segment_ids (TensorProto): graph input of token type IDs
        input_mask (TensorProto): graph input of attention mask
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type (int): mask type 1 is mask index; 2 is 2D mask; 3 is key len, cumulated lengths of query and key

    Returns:
        List[Dict[str,numpy.ndarray]]: list of test cases, where each test case is a dictionary
                                       with input name as key and a tensor as value
    Nr   zExample inputs)r   r   r   seedr/   r!   rH   r%   r4   lenr?   append)r   r   rK   r   rL   rM   r
   r"   r+   r'   r,   r-   
all_inputsZ
_test_caseZinput_1r6   r   r   r    fake_test_data   s,    "

     

rR   )r   r   rK   rN   rL   r
   r"   r+   r'   r,   r-   c                 C   s:   d}t | ||||||||||	|
}t||kr6td |S )a  Create given number of input data for testing

    Args:
        batch_size (int): batch size
        sequence_length (int): sequence length
        test_cases (int): number of test cases
        seed (int): random seed
        verbose (bool): print more information or not
        input_ids (TensorProto): graph input of input IDs
        segment_ids (TensorProto): graph input of token type IDs
        input_mask (TensorProto): graph input of attention mask
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type (int): mask type 1 is mask index; 2 is 2D mask; 3 is key len, cumulated lengths of query and key

    Returns:
        List[Dict[str,numpy.ndarray]]: list of test cases, where each test case is a dictionary
                                       with input name as key and a tensor as value
    i'  z$Failed to create test data for test.)rR   rO   r?   )r   r   rK   rN   rL   r
   r"   r+   r'   r,   r-   r   rQ   r   r   r    generate_test_data   s$     rS   c                 C   s`   |t |jkrd S |j| }| |}|d kr\| ||}|d k	r\|jdkr\| |jd }|S )NZCastr   )rO   inputfind_graph_inputZ
get_parentZop_type)
onnx_model
embed_nodeZinput_indexrT   Zgraph_inputZparent_noder   r   r    get_graph_input_from_embed_node%  s    

rX   )rV   input_ids_namesegment_ids_nameinput_mask_namer   c                 C   s  |   }|dk	r| |}|dkr0td| d}|rX| |}|dkrXtd| d}|r| |}|dkrtd| d|rdnd |rdnd }t||krtd| dt| |||fS t|dkrtdt| | d	}	t|	dkrv|	d }
t| |
d}t| |
d}t| |
d
}|dkrZ|D ]}|j }d|kr:|}q:|dkrltd|||fS d}d}d}|D ]>}|j }d|kr|}nd|ksd|kr|}n|}q|r|r|r|||fS tddS )a  Find graph inputs for BERT model.
    First, we will deduce inputs from EmbedLayerNormalization node.
    If not found, we will guess the meaning of graph inputs based on naming.

    Args:
        onnx_model (OnnxModel): onnx model object
        input_ids_name (str, optional): Name of graph input for input IDs. Defaults to None.
        segment_ids_name (str, optional): Name of graph input for segment IDs. Defaults to None.
        input_mask_name (str, optional): Name of graph input for attention mask. Defaults to None.

    Raises:
        ValueError: Graph does not have input named of input_ids_name or segment_ids_name or input_mask_name
        ValueError: Expected graph input number does not match with specified input_ids_name, segment_ids_name
                    and input_mask_name

    Returns:
        Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]: input tensors of input_ids,
                                                                                 segment_ids and input_mask
    Nz Graph does not have input named r(   r   zExpect the graph to have z inputs. Got r.   z'Expect the graph to have 3 inputs. Got ZEmbedLayerNormalization   maskz#Failed to find attention mask inputtokensegmentz?Fail to assign 3 inputs. You might try rename the graph inputs.)Z'get_graph_inputs_excluding_initializersrU   
ValueErrorrO   Zget_nodes_by_op_typerX   rH   lower)rV   rY   rZ   r[   Zgraph_inputsr
   r"   r+   Zexpected_inputsZembed_nodesrW   rT   Zinput_name_lowerr   r   r    find_bert_inputs2  sf    












rb   )	onnx_filerY   rZ   r[   r   c              	   C   s@   t  }t| d}||  W 5 Q R X t|}t||||S )a  Find graph inputs for BERT model.
    First, we will deduce inputs from EmbedLayerNormalization node.
    If not found, we will guess the meaning of graph inputs based on naming.

    Args:
        onnx_file (str): onnx model path
        input_ids_name (str, optional): Name of graph input for input IDs. Defaults to None.
        segment_ids_name (str, optional): Name of graph input for segment IDs. Defaults to None.
        input_mask_name (str, optional): Name of graph input for attention mask. Defaults to None.

    Returns:
        Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]: input tensors of input_ids,
                                                                                 segment_ids and input_mask
    rb)r   rC   ZParseFromStringreadr	   rb   )rc   rY   rZ   r[   modelrI   rV   r   r   r    get_bert_inputs  s
    rg   c                  C   sJ  t  } | jddtdd | jddtd dd | jd	dtd
dd | jddtddd | jddtd dd | jddtd dd | jddtd dd | jddtd
dd | jddtddd | jddddd | jdd | jdddd d | jdd! | jd"d#d$td%d& | jd'd(ddd)d | jdd* | jd+dtd,d-d |  }|S ).Nz--modelTzbert onnx model path.)requiredr   helpz--output_dirFz4output test data path. Default is current directory.)rh   r   defaultri   z--batch_sizer(   zbatch size of inputz--sequence_length   z maximum sequence length of inputz--input_ids_namezinput name for input idsz--segment_ids_namezinput name for segment idsz--input_mask_namezinput name for attention maskz	--samplesz$number of test cases to be generatedz--seedr.   zrandom seedz	--verbose
store_truezprint verbose information)rh   actionri   )rL   z--only_input_tensorsz-only save input tensors and no output tensors)only_input_tensorsz-az--average_sequence_lengthz)average sequence length excluding padding)rj   r   ri   z-rz--random_sequence_lengthz3use uniform random instead of fixed sequence length)r,   z--mask_typer)   z^mask type: (1: mask index, 2: raw 2D mask, 3: key lengths, cumulated lengths of query and key))argparseArgumentParseradd_argumentstrintset_defaults
parse_args)parserargsr   r   r    parse_arguments  s    ry   )rf   
output_dirr   r   rK   rN   rL   rY   rZ   r[   rn   r'   r,   r-   c                 C   sB  t | |||	\}}}t|||||||||||}t|D ](\}}tj|dt| }t|| q8|
rjdS ddl}d|	 krddgndg}|j
| |d}dd | D }t|D ]\}}tj|dt| }|||}t|D ]T\}}tt|| |}ttj|d	| d
d}||  W 5 Q R X qqdS )aI  Create test data for a model, and save test data to a directory.

    Args:
        model (str): path of ONNX bert model
        output_dir (str): output directory
        batch_size (int): batch size
        sequence_length (int): sequence length
        test_cases (int): number of test cases
        seed (int): random seed
        verbose (bool): whether print more information
        input_ids_name (str): graph input name of input_ids
        segment_ids_name (str): graph input name of segment_ids
        input_mask_name (str): graph input name of input_mask
        only_input_tensors (bool): only save input tensors,
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type(int): mask type
    Ztest_data_set_Nr   ZCUDAExecutionProviderZCPUExecutionProvider)	providersc                 S   s   g | ]
}|j qS r   )rH   ).0outputr   r   r    
<listcomp>O  s     z-create_and_save_test_data.<locals>.<listcomp>Zoutput_r8   r9   )rg   rS   r@   r:   r;   rD   rs   rJ   onnxruntimeZget_available_providersZInferenceSessionget_outputsrunr   rB   r   ZasarrayrC   rE   rF   )rf   rz   r   r   rK   rN   rL   rY   rZ   r[   rn   r'   r,   r-   r
   r"   r+   rQ   r1   r6   r5   r   r{   sessionZoutput_namesresultZoutput_nameZtensor_resultrI   r   r   r    create_and_save_test_data  sB    "

r   c                  C   s   t  } | jdkr| j| _| j}|d krPt| j}tj|j	d| j
 d| j }|d k	rpt|}|jddd ntd t| j|| j
| j| j| j| j| j| j| j| j| j| j| j td| d S )Nr   Zbatch_Z_seq_T)parentsexist_okz7Directory existed. test data files will be overwritten.z Test data is saved to directory:)ry   r'   r   rz   r   rf   r:   r;   rD   parentr   r=   r?   r   ZsamplesrN   rL   rY   rZ   r[   rn   r,   r-   )rx   rz   pr;   r   r   r    mainZ  s8    

 r   __main__)r)   )NNN)NNN)#rp   r:   r   pathlibr   typingr   r   r   numpyr   Zonnxr   r   r   rV   r	   rt   Zndarrayr!   r%   r*   boolr4   rs   rJ   rR   rS   rX   rb   rg   ry   r   r   __name__r   r   r   r    <module>	   s       I;4   ^   eL'
