U
    T?h;                     @   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re  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__ Z/var/www/html/venv/lib/python3.8/site-packages/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 ]}|j|kr
|jjjjd }||_|jjjjd }|	drP||_q
|	dr
|j|kr
t
d| dd d|j d|j d		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 z
 for axis z of z'. Contradicts existing dimension value .N)r   r%   r.   r   typetensor_typer    dimr>   ZHasField
ValueError)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rt|}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-   rH   )	AssertionErrorr   randomrandintZint32ZonesZzerosZfloat32r   )r   	input_idssegment_ids
input_maskrD   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)	providersrH   zshapes=)"Zbert_inputsr9   r+   r/   r3   lenr<   rE   openwriter   SerializeToStringonnxruntimeZSessionOptionsZGraphOptimizationLevelZORT_DISABLE_ALLZgraph_optimization_levelZInferenceSessionr%   r.   r   r@   rA   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   rD   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   rJ   r	   Zto_arrayr[   rU   r)   r\   r]   )r   rf   rh   ri   rc   Ztarget_shapeinitializerZsource_shapeZnew_target_shaperg   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(   rl   )r   r2   rm   r   r   r   r   validate_outputs  s    z,BertOnnxModelShapeOptimizer.validate_outputsNF)output_pathrM   rN   rO   ra   rb   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rtd d S t P}dt d}|
rd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   3  s     z8BertOnnxModelShapeOptimizer.optimize.<locals>.<listcomp>   z9Skip shape optimization since graph input number is not 3ztemp_{}.onnxz%m_%d-%H_%M_%Sr?   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%   rk   r   
startswithr   r\   inforn   ro   r`   r(   rU   Z'get_graph_inputs_excluding_initializerstempfileTemporaryDirectoryformatr   nowstrftimeospathjoinrj   r]   warningrV   rW   rX   )r   rp   rM   rN   rO   ra   rb   r2   rD   rP   rc   r!   Zremaining_outputstemp_dirZtemp_file_namedir	temp_filerd   r   r   r   optimize  sL    









z$BertOnnxModelShapeOptimizer.optimize)r-   r=   )rF   )Nr-   r=   F)__name__
__module____qualname____doc__r   r"   r+   r/   r3   r<   rE   rR   rj   r^   strrn   r   ro   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)ra   z--enable_reshape_opt)rb   z	--verbose)rc   )argparseArgumentParseradd_argumentr   intset_defaults
parse_args)parserargsr   r   r   parse_argumentsT  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)rc   Zlog_handlerZlogging_levelr   r   r   setup_loggingh  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   rc   r2   splitr   rV   r.   ZParseFromStringreadr
   r   r   r(   rM   rN   rO   ra   rb   rD   rP   )r   r2   r   Z
input_filer   Z	optimizerr   r   r   mainu  s(    
r   __main__) r   r   ry   rer   rt   collectionsr   r   pathlibr   typingr   r   numpyr   r   r   r   r	   r   r
   rY   	getLoggerr   r\   r   r1   r   r   r   r   r   r   r   r   <module>
   s2   
  4