U
    “±ËhD;  ã                   @   sè   d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZ d dlmZ d dl	m
Z
 d dlmZmZ d dlZd dlZd dlmZmZmZ d dlmZ d dlZe e¡ZdZd	ZG d
d„ deƒZdd„ Zdd„ Zdd„ Zedkräeƒ  dS )é    N)Údeque)Údatetime)ÚPath)ÚListÚOptional)Ú
ModelProtoÚTensorProtoÚnumpy_helper)Ú	OnnxModelZconstant_shape_opt__Zreshape_input_shape__c                
       s°   e Zd ZdZ‡ fdd„Zdd„ Zdd„ Zdd	„ Zd
d„ Zdd„ Z	d$dd„Z
d%dd„Zdd„ Zdd„ Zedœdd„Zee dœdd„Zd&eeeeeeeee  d!œd"d#„Z‡  ZS )'ÚBertOnnxModelShapeOptimizerz£
    This optimizer will replace Shape output or the shape input of Reshape node by initializer. Currently, it requires
    model inputs to have static shape.
    c                    s   t ƒ  |j¡ d S )N)ÚsuperÚ__init__Úmodel)ÚselfÚ
onnx_model©Ú	__class__© úL/tmp/pip-unpacked-wheel-socb9apf/onnxruntime/transformers/shape_optimizer.pyr   (   s    z$BertOnnxModelShapeOptimizer.__init__c                 C   sB   t j|t jd}|  dt¡}tjj|tj	|j
|d}|  |¡ |S )z8
        Add an initializer for constant shape.
        ©ÚdtypeÚConstant)ÚnameZ	data_typeZdimsÚvals)ÚnpZasarrayÚint64Úcreate_node_nameÚCONSTANT_SHAPE_NAME_PREFIXÚonnxÚhelperZmake_tensorr   ZINT64ÚshapeZadd_initializer)r   r    Zshape_valueZconstant_shape_nameÚtensorr   r   r   Úadd_shape_initializer+   s    ü
z1BertOnnxModelShapeOptimizer.add_shape_initializerc                 C   sH   |   ¡ }g }| jjjD ],}|jdkr|jd |kr| |jd ¡ q|S )zD
        Returns a list of output names of all Shape nodes.
        ÚShaper   )Úinput_name_to_nodesr   ÚgraphÚnodeÚop_typeÚoutputÚappend)r   r$   Úoutputsr&   r   r   r   Úget_shape_outputs:   s    
z-BertOnnxModelShapeOptimizer.get_shape_outputsc                 C   s:   |   ¡  g }| jjjD ]}|jdkr| |jd ¡ q|S )zG
        Returns a list of shape input names of Reshape nodes.
        ÚReshapeé   )Zoutput_name_to_noder   r%   r&   r'   r)   Úinput)r   Zshape_inputsr&   r   r   r   Úget_reshape_shape_inputsH   s    
z4BertOnnxModelShapeOptimizer.get_reshape_shape_inputsc                 C   sp   g }g }| j jjD ]N}|jdkr|jd }|  dt¡}tjj	d|g|gd}| 
|¡ | 
|¡ q|  |¡ |S )zˆ
        For each Reshape node, create a Shape node for its first input.
        Returns the output names of these Shape nodes.
        r,   r   ZReshape_Inputr#   )Úinputsr*   )r   r%   r&   r'   r.   r   ÚRESHAPE_INPUT_SHAPE_PREFIXr   r   Z	make_noder)   Z	add_nodes)r   Úoutput_namesZnodes_to_addr&   r.   Zoutput_nameZ
shape_noder   r   r   Úadd_shape_for_reshape_inputU   s    



z7BertOnnxModelShapeOptimizer.add_shape_for_reshape_inputc                 C   st   g }dd„ | j jjD ƒ}|D ]R}|  |¡dk	r0q| |¡ ||krtj ¡ }||_| j jj 	|g¡ | |¡ q|S )z=
        Add a list of output names to graph output.
        c                 S   s   g | ]
}|j ‘qS r   ©r   ©Ú.0r(   r   r   r   Ú
<listcomp>l   s     zFBertOnnxModelShapeOptimizer.add_extra_graph_output.<locals>.<listcomp>N)
r   r%   r(   Úget_initializerr)   r   r   ZValueInfoProtor   Úextend)r   Úextra_outputsÚnames_to_evaluater2   r   Zoutput_infor   r   r   Úadd_extra_graph_outputg   s    

z2BertOnnxModelShapeOptimizer.add_extra_graph_outputr-   é€   c                 C   s‚   | j jjD ]r}|j|kr
|jjjjd }||_|jjjjd }| 	d¡rP||_q
| 	d¡r
|j|kr
t
d |d|j|j¡ƒ‚q
dS )z_
        Update the model to use static axes instead of dynamic axes for graph inputs.
        r   r-   Z	dim_paramÚ	dim_valuez_Unable to set dimension value to {} for axis {} of {}. Contradicts existing dimension value {}.N)r   r%   r.   r   ÚtypeÚtensor_typer    Zdimr>   ZHasFieldÚ
ValueErrorÚformat)r   r0   Ú
batch_sizeZmax_seq_lenr.   Z	dim_protor   r   r   Úuse_static_input{   s     

   ÿÿz,BertOnnxModelShapeOptimizer.use_static_inputé   c                 C   s®   |dkst ‚tjj|||ftjd}tj||ftjd}	tj||ftjd}
|dkrtt |¡}t |	¡}	t |
¡}
n&|dkršt |¡}t |	¡}	t |
¡}
||||	||
i}|S )z›
        Create dummy data for model inputs. If the model has more than 3 inputs, please update this function accordingly before running the tool.
        )r-   é   é   )Úsizer   r   r-   rG   )	ÚAssertionErrorr   ÚrandomÚrandintZint32ZonesÚzerosZfloat32r   )r   Ú	input_idsÚsegment_idsÚ
input_maskrC   Úsequence_lengthÚ	elem_typeZdictionary_sizeZinput_1Zinput_2Zinput_3r0   r   r   r   Úcreate_dummy_inputs   s    




z/BertOnnxModelShapeOptimizer.create_dummy_inputsc              	   C   s¨  |||g| _ g }|r"| |  ¡ ¡ |	rJ|  ¡ }|  ¡ }| |¡ | |¡ t|ƒdkrZd S |  |¡}|  | j ||¡ t|dƒ}| 	| j
 ¡ ¡ W 5 Q R X t ¡ }tjj|_tj||ddgd}d}| j
jjD ]}|j|krÎ|jjj}qÎ|  ||||||¡}| ||¡}i }t|ƒD ]\}}|| ||< qt d|› ¡ |	rnt|ƒD ]"\}}|| }|  ||||
¡ qJ| ¡ D ]"\}}|  |¡}|   ||j¡ qv|  !|¡ d S )Nr   ÚwbZCUDAExecutionProviderZCPUExecutionProvider)Z	providersrG   zshapes=)"Zbert_inputsr9   r+   r/   r3   Úlenr<   rD   ÚopenÚwriter   ÚSerializeToStringÚonnxruntimeZSessionOptionsZGraphOptimizationLevelZORT_DISABLE_ALLZgraph_optimization_levelZInferenceSessionr%   r.   r   r?   r@   rQ   rR   ÚrunÚ	enumerateÚloggerÚdebugÚupdate_target_shapeÚitemsr"   Zreplace_input_of_all_nodesÚprune_graph)r   Ztemp_model_pathrM   rN   rO   r2   rC   rP   Úenable_shape_optÚenable_reshape_optÚverboser:   Zreshape_shape_inputsZreshape_input_shapesr;   ÚoutZsess_optionsÚsessionrQ   r.   r0   r*   ÚshapesÚir   Úshape_inputÚinput_shaper    r!   r   r   r   Úshape_optimization®   sP    



ý

z.BertOnnxModelShapeOptimizer.shape_optimizationc                 C   sÐ   ||kr|| }n |   |¡}|dk	s(t‚t |¡}||krD|| }n |   |¡}|dk	sZt‚t |¡}g }t|ƒD ]6\}	}
|	t|ƒk rœ||	 |
krœ| d¡ qp| |
¡ qp|||< t d|› d|› d|› ¡ dS )zð
        Update the target shape to use 0 to represent that dimension value does not change.
        For example, shape of source data is (2, 5, 8) and target shape is (2, 5, 4, 2), the target shape will be updated to (0, 0, 4, 2).
        Nr   zsource_shape=z, target_shape=z, new_target_shape=)	r8   rI   r	   Zto_arrayrZ   rT   r)   r[   r\   )r   re   rg   rh   rb   Ztarget_shapeÚinitializerZsource_shapeZnew_target_shaperf   r>   r   r   r   r]   ò   s"    





z/BertOnnxModelShapeOptimizer.update_target_shape)r.   c                 C   s6   |   |¡s2dd„ | jjjD ƒ}td|› d|› ƒ‚d S )Nc                 S   s   g | ]
}|j ‘qS r   r4   )r6   r.   r   r   r   r7     s     z>BertOnnxModelShapeOptimizer.validate_input.<locals>.<listcomp>zInput z% does not exist in the graph inputs: )Zfind_graph_inputr   r%   r.   Ú	Exception)r   r.   Úvalid_namesr   r   r   Úvalidate_input  s    
z*BertOnnxModelShapeOptimizer.validate_input)r2   c                 C   s>   dd„ | j jjD ƒ}|D ] }||krtd|› d|› ƒ‚qd S )Nc                 S   s   g | ]
}|j ‘qS r   r4   r5   r   r   r   r7     s     z@BertOnnxModelShapeOptimizer.validate_outputs.<locals>.<listcomp>zOutput z& does not exist in the graph outputs: )r   r%   r(   rk   )r   r2   rl   r   r   r   r   Úvalidate_outputs  s    z,BertOnnxModelShapeOptimizer.validate_outputsNF)Úoutput_pathrM   rN   rO   r`   ra   r2   c                 C   sZ  | j jjD ] }|j t¡r
t d¡  d S q
|  |¡ |  |¡ |  |¡ |d k	rf|  	|¡ |  
|¡ dd„ | j jjD ƒ}|s„|r&t|  ¡ ƒdkr¢t d¡ d S t ¡ P}d t ¡  d¡¡}|
rÈdn|}tj ||¡}|  |||||||	|||
¡
 W 5 Q R X t d	|› ¡ t d
|› d|	› ¡ |d k	rVt|dƒ}| | j  ¡ ¡ W 5 Q R X d S )Nz5Skip shape optimization since it has been done beforec                 S   s   g | ]
}|j ‘qS r   r4   r5   r   r   r   r7   5  s     z8BertOnnxModelShapeOptimizer.optimize.<locals>.<listcomp>é   z9Skip shape optimization since graph input number is not 3ztemp_{}.onnxz%m_%d-%H_%M_%SÚ.z$Temp model with additional outputs: zZShape optimization is done. The optimized model might only work for input with batch_size=z sequence_length=rS   )r   r%   rj   r   Ú
startswithr   r[   Úinform   rn   r_   r(   rT   Z'get_graph_inputs_excluding_initializersÚtempfileÚTemporaryDirectoryrB   r   ÚnowÚstrftimeÚosÚpathÚjoinri   r\   ÚwarningrU   rV   rW   )r   ro   rM   rN   rO   r`   ra   r2   rC   rP   rb   r!   Zremaining_outputsÚtemp_dirZtemp_file_nameÚdirÚ	temp_filerc   r   r   r   Úoptimize  sL    








öÿ
z$BertOnnxModelShapeOptimizer.optimize)r-   r=   )rE   )Nr-   r=   F)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r"   r+   r/   r3   r<   rD   rR   ri   r]   Ústrrm   r   rn   Úboolr   r   Ú__classcell__r   r   r   r   r   "   s6   
 ø
!D    õ
ør   c                  C   sî   t  ¡ } | jddtd | jddtd | jddtd | jddtd | jddtd | jdd	td d
 | jdd	tdd
 | jdd	tdd
 | jdd	dd | jd	d | jdd	dd | jd	d | jdd	dd | jd	d |  ¡ }|S )Nz--inputT)Úrequiredr?   z--outputz--input_idsz--segment_idsz--input_maskz--output_namesF)r‡   r?   Údefaultz--batch_sizer-   z--sequence_lengthr=   z--enable_shape_optÚ
store_true)r‡   Úaction)r`   z--enable_reshape_opt)ra   z	--verbose)rb   )ÚargparseÚArgumentParserÚadd_argumentr„   ÚintÚset_defaultsÚ
parse_args)ÚparserÚargsr   r   r   Úparse_argumentsV  s"    r“   c                 C   s`   t  tj¡}| r(| t  d¡¡ t j}n| t  d¡¡ t j}| |¡ t	 
|¡ t	 |¡ d S )Nz8[%(filename)s:%(lineno)s - %(funcName)20s()] %(message)sz%(filename)20s: %(message)s)ÚloggingÚStreamHandlerÚsysÚstdoutÚsetFormatterÚ	FormatterÚDEBUGÚINFOÚsetLevelr[   Ú
addHandler)rb   Zlog_handlerZlogging_levelr   r   r   Úsetup_loggingj  s    

rž   c                  C   s˜   t ƒ } t| jƒ | jd krd n
| j d¡}tƒ }t| jdƒ}| | 	¡ ¡ W 5 Q R X t
|ƒ}t|ƒ}| | j| j| j| j| j| j|| j| j| j¡
 d S )Nú;Úrb)r“   rž   rb   r2   Úsplitr   rU   r.   ZParseFromStringÚreadr
   r   r   r(   rM   rN   rO   r`   ra   rC   rP   )r’   r2   r   Z
input_filer   Z	optimizerr   r   r   Úmainw  s(    
ör£   Ú__main__) r‹   r”   rx   Úrer–   rt   Úcollectionsr   r   Úpathlibr   Útypingr   r   Znumpyr   r   r   r   r	   r   r
   rX   Ú	getLoggerr€   r[   r   r1   r   r“   rž   r£   r   r   r   r   Ú<module>
   s2   
  6