o
    iY                     @   st  d dl Z d dlZd dlZd dlmZ d dlZd dlmZm	Z	m
Z
 d dlmZ de	dededed	ejf
d
dZde	deded	ejfddZdedefddZ	d;de	dededededed	ejfddZdedeeejf fddZdedededededede	de	de	dededefd d!Z	"d<dededed#edede	de	de	dedededefd$d%Zd&d' Z			d=d(ed)edB d*edB d+edB d	eejdB ejdB ejdB f f
d,d-Z			d=d.ed)edB d*edB d+edB d	eejdB ejdB ejdB f f
d/d0Zd1d2 Zd3ed4edededed#eded)edB d*edB d+edB d5edededefd6d7Zd8d9 Ze d:kr8e  dS dS )>    N)Path)
ModelProtoTensorProtonumpy_helper)	OnnxModel	input_ids
batch_sizesequence_lengthdictionary_sizereturnc                 C   sr   | j jjtjtjtjfv sJ tjj	|||ftj
d}| j jjtjkr*t|}|S | j jjtjkr7t|}|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nprandomrandintint32float32int64)r   r   r	   r
   data r   a/home/kim/smarthome/.venv/lib/python3.10/site-packages/onnxruntime/transformers/bert_test_data.pyfake_input_ids_data   s   


r   segment_idsc                 C   sn   | j jjtjtjtjfv sJ tj||ftj	d}| j jjtjkr(t
|}|S | j jjtjkr5t|}|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   zerosr   r   r   )r   r   r	   r   r   r   r   fake_segment_ids_data1   s   


r!   max_sequence_lengthaverage_sequence_lengthc                 C   sH   |dkr|| ks
J d| | krt d| |  | S t dd| d S )N      )r   r   )r"   r#   r   r   r   get_random_lengthL   s   r&   r%   
input_maskrandom_sequence_length	mask_typec                 C   sJ  | j jjtjtjtjfv sJ |dkr8tj|tj	d}|r,t
|D ]	}t||||< q!nt
|D ]}|||< q0n|dkr}tj||ftj	d}|rbt
|D ]}t||}t
|D ]}	d|||	f< qWqLntj||ftj	d}
|
|d|
jd d|
jd f< n|dksJ tj|d d tj	d}|rt
|D ]	}t||||< qt
|d D ]6}|dkr||| d  ||d   nd||| < |dkr||| d  ||d   nd|d| d | < qn(t
|D ]}|||< qt
|d D ]}|| ||| < || |d| d | < q| j jjtjkrt|}|S | j jjtjkr#t|}|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   Zonesr   ranger&   r    shaper   r   )r'   r   r	   r#   r(   r)   r   iZactual_seq_lenjtempr   r   r   fake_input_mask_dataV   sZ   


"0:


r0   	directoryinputsc              	   C   s   t j| s(zt |  W n ty   td|  d Y nw td|  d ntd|  d t| D ]1\}\}}t	||}t
t j| d| dd	}||  W d
   n1 sbw   Y  q6d
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)r1   r2   indexnamer   Ztensorfiler   r   r   output_test_data   s   rF   
test_casesverboserandom_seedc                 C   s   |dusJ t j| t| g }t|D ]9}t|| ||}|j|i}|r0t|| |||j< |r>t|| ||	|
|||j< |rKt|dkrKt	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   seedr+   r   rD   r!   r0   lenr;   append)r   r	   rG   r
   rH   rI   r   r   r'   r#   r(   r)   
all_inputsZ
_test_caseZinput_1r2   r   r   r   fake_test_data   s"   "



rN   '  rJ   c                 C   s6   t | ||||||||||	|
}t||krt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
    z$Failed to create test data for test.)rN   rK   r;   )r   r	   rG   rJ   rH   r   r   r'   r#   r(   r)   r
   rM   r   r   r   generate_test_data   s"   !rP   c                 C   s`   |t |jkr	d S |j| }| |}|d u r.| ||}|d ur.|jdkr.| |jd }|S )NZCastr   )rK   inputfind_graph_inputZ
get_parentZop_type)
onnx_model
embed_nodeZinput_indexrQ   Zgraph_inputZparent_noder   r   r   get_graph_input_from_embed_node$  s   

rU   rS   input_ids_namesegment_ids_nameinput_mask_namec                 C   s  |   }|dure| |}|du rtd| d}|r,| |}|du r,td| d}|r@| |}|du r@td| d|rEdnd |rKdnd }t||kr`td| dt| |||fS t|dkrttdt| | d	}	t|	dkr|	d }
t| |
d}t| |
d}t| |
d
}|du r|D ]}|j }d|v r|}q|du rtd|||fS d}d}d}|D ]}|j }d|v r|}qd|v sd|v r|}q|}q|r|r|r|||fS td)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_initializersrR   
ValueErrorrK   Zget_nodes_by_op_typerU   rD   lower)rS   rV   rW   rX   Zgraph_inputsr   r   r'   Zexpected_inputsZembed_nodesrT   rQ   Zinput_name_lowerr   r   r   find_bert_inputs1  sb   








r_   	onnx_filec                 C   sT   t  }t| d}||  W d   n1 sw   Y  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
    rbN)r   r?   ZParseFromStringreadr   r_   )r`   rV   rW   rX   modelrE   rS   r   r   r   get_bert_inputs  s   rd   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.)re   r   defaultrf   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)re   actionrf   )rH   z--only_input_tensorsz-only save input tensors and no output tensors)only_input_tensorsz-az--average_sequence_lengthz)average sequence length excluding padding)rg   r   rf   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   rv   rc   
output_dirrk   c                 C   sV  t | |||	\}}}t|||||||||||}t|D ]\}}tj|dt| }t|| q|
r5dS ddl}d|	 v rCddgndg}|j
| |d}dd | D }t|D ]N\}}tj|dt| }|||}t|D ]4\}}tt|| |}ttj|d	| d
d}||  W d   n1 sw   Y  qsqZd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   )rD   ).0outputr   r   r   
<listcomp>N  s    z-create_and_save_test_data.<locals>.<listcomp>Zoutput_r4   r5   )rd   rP   r<   r6   r7   r@   rp   rF   onnxruntimeZget_available_providersZInferenceSessionZget_outputsrunr   r>   r   Zasarrayr?   rA   rB   )rc   rw   r   r	   rG   rJ   rH   rV   rW   rX   rk   r#   r(   r)   r   r   r'   rM   r-   r2   r1   r|   rx   sessionZoutput_namesresultZoutput_nameZtensor_resultrE   r   r   r   create_and_save_test_data  sJ   "r   c                  C   s   t  } | jdkr| j| _| j}|d u r(t| j}tj|j	d| j
 d| j }|d ur8t|}|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:)rv   r#   r	   rw   r   rc   r6   r7   r@   parentr   r9   r;   r   ZsamplesrJ   rH   rV   rW   rX   rk   r(   r)   )ru   rw   pr7   r   r   r   mainY  s8   

 r   __main__)r%   )rO   )NNN)!rm   r6   r   pathlibr   numpyr   Zonnxr   r   r   rS   r   rq   Zndarrayr   r!   r&   boolr0   rp   dictrF   rN   rP   rU   tupler_   rd   rv   r   r   __name__r   r   r   r   <module>   s.  

I	

F	

4
^
d	

L
'
