o
    7?e>                     @   sX   d Z ddlZddlm  mZ ddlmZ ddlm	Z	 edg dG dd de	j
ZdS )	z#Base class for Python-based metrics    N)keras_export)base_metricz#keras.metrics.experimental.PyMetric)v1c                       sP   e Zd ZdZd fdd	Z fddZdddZd	d
 Zdd Zdd Z	  Z
S )PyMetrica  Metric which runs in Python, compiled outside of the TensorFlow graph.

    Args:
      name: (Optional) string name of the PyMetric instance.
      dtype: (Optional) data type of the PyMetric result.
      **kwargs: Additional layer keywords arguments.

    Usage of `PyMetric` is generally identical to `keras.metrics.Metric`.
    It can be used in isolation, or in tandem with the `compile()` API. For more
    information about the usage of `PyMetric`, see `keras.metrics.Metric`.

    Unlike regular metrics, `PyMetric` instances are outside-compiled
    with respect to the TensorFlow graph during training or evaluation.
    They have access to the same
    inputs of a standard in-graph metric, but they run in a Python interpreter
    on the host CPU. Any data stored in a `PyMetric` is located on the main
    memory of the host CPU, and any TensorFlow ops used in a PyMetric are
    run eagerly on the host CPU.

    As a result, `PyMetric` instances are generally not as performant
    as in-graph metrics, and should only be used in cases where computing
    the metric inside of the TensorFlow graph is either impossible
    or prohibitively expensive.

    **Note:** Due to the use of `tf.py_function`, PyMetrics
    are incompatible with XLA and therefore TPUs.

    Methods to be implemented by subclasses:

    * `update_state()`: Handles updates to internal state variables
    * `result()`: Computes and returns a scalar value or a dict of scalar values
      for the metric from the state variables.
    * `reset_state()`: Computes and returns a scalar value for the metric from
      the state variables.

    This subclass implementation is similar to that of `keras.metrics.Metric`,
    with two notable differences:

    * Inputs to `update_state()` in a `PyMetric` are eager tensors, and both
    `update_state()` and `result()` run outside of the TensorFlow graph,
    executing any TensorFlow ops eagerly.
    * `reset_state()` is also called at initialization time to initialize the
    Python state of the metric.
    * `result()` can only return a single scalar. It does not support returning
    a dictionary of results like `keras.metrics.Metric`.

    Example subclass implementation using sklearn's Jaccard Score:

    ```python
    from sklearn.metrics import jaccard_score
    import tensorflow as tf

    class JaccardScore(tf.keras.metrics.experimental.PyMetric):

      def __init__(self, name='jaccard_score', **kwargs):
        super().__init__(name=name, **kwargs)

      def update_state(self, y_true, y_pred, sample_weight=None):
        self.jaccard_sum += jaccard_score(y_pred, y_true, average="macro")
        self.count += 1

      def reset_state(self):
        self.jaccard_sum = 0.
        self.count = 0.

      def result(self):
        return self.jaccard_sum / self.count
    ```
    Nc                    s$   t  jd||d| |   d S )N)namedtype )super__init__reset_state)selfr   r   kwargs	__class__r   \/home/www/facesmatcher.com/pyenv/lib/python3.10/site-packages/keras/src/metrics/py_metric.pyr
   a   s   zPyMetric.__init__c                    sz   t tj| |   jd	fdd	}| _d	dd}t|  _ jfdd}| _	 fdd}t|  _ S )
Nc                    s:   t d  | ||W  d    S 1 sw   Y  d S Nz/cpu:0tfZdevice)y_truey_predsample_weight)obj_update_stater   r   update_state_on_cpuk   s   
$z-PyMetric.__new__.<locals>.update_state_on_cpuc                 S   s,   ||g}|d ur| | tj| j|g dS )N)funcinpTout)appendr   py_functionr   )r   r   r   r   Zeager_inputsr   r   r   update_state_fnq   s   
z)PyMetric.__new__.<locals>.update_state_fnc                      s4   t d   W  d    S 1 sw   Y  d S r   r   r   )
obj_resultr   r   result_on_host_cpu~   s   $z,PyMetric.__new__.<locals>.result_on_host_cpuc                    s   t j| jg  jdS )N)r   r   )r   r   r    r   r   )objr   r   	result_fn   s   
z#PyMetric.__new__.<locals>.result_fnN)
r	   r   Metric__new__update_stater   types
MethodTyperesultr    )clsargsr   r   r   r    r#   r   )r"   r   r   r   r&   e   s   
zPyMetric.__new__c                 C      t d)a  Accumulates statistics for the metric.

        **Note:** This function is executed outside of the TensorFlow graph
        on the CPU host.

        This means:

        a) Inputs are eager tensors.
        b) Any TensorFlow ops run in this method are run eagerly.
        c) Any Tensors created are allocated to the CPU's main memory.

        Args:
          y_true: Target output
          y_pred: Predicted output
          sample_weight: (Optional) weights for the individual samples in
            `y_true` and `y_pred`
        z*Subclasses should implement `update_state`NotImplementedError)r   r   r   r   r   r   r   r'      s   zPyMetric.update_statec                 C   r-   )zMerges the state from one or more metrics.

        `PyMetric` instances that intend to support merging state must override
         this method, as the default implementation
        in `keras.metrics.Metric` does not apply to `PyMetric`.
        z)Subclasses should implement `merge_state`r.   )r   Zmetricsr   r   r   merge_state   s   zPyMetric.merge_statec                 C   r-   )zResets all of the metric state variables.

        This function is called between epochs when a metric is evaluated during
        training. It's also called when the metric is initialized.
        z)Subclasses should implement `reset_state`r.   r!   r   r   r   r      s   zPyMetric.reset_statec                 C   r-   )a  Computes and returns the scalar metric value.

        **Note:** This function is executed outside of the TensorFlow graph
         on the CPU host. This means any TensorFlow ops run in this method
         are run eagerly.

        Result computation is an idempotent operation that simply calculates the
        metric value using the state variables.

        Returns:
            A Python scalar.
        z$Subclasses should implement `result`r.   r!   r   r   r   r*      s   zPyMetric.result)NNr$   )__name__
__module____qualname____doc__r
   r&   r'   r0   r   r*   __classcell__r   r   r   r   r      s    F
(	r   )r4   r(   Ztensorflow.compat.v2compatv2r   Z tensorflow.python.util.tf_exportr   Zkeras.src.metricsr   r%   r   r   r   r   r   <module>   s   
