예제 #1
0
    def process_event(self, inputs: Union[List, Dict],
                      headers: Dict) -> ModelResponse:
        """
        Process the event and return Alibi Detect score

        Parameters
        ----------
        inputs
             Input data
        headers
             Header options

        Returns
        -------
             Alibi Detect response

        """
        logging.info("PROCESSING EVENT.")
        logging.info(str(headers))
        logging.info("----")
        try:
            X = read_inputs_as_numpy(inputs)
        except Exception as e:
            raise Exception(
                "Failed to initialize NumPy array from inputs: %s, %s" %
                (e, inputs))

        ret_instance_score = True
        if (HEADER_RETURN_INSTANCE_SCORE in headers
                and headers[HEADER_RETURN_INSTANCE_SCORE] == "false"):
            ret_instance_score = False

        ad_preds = self.model.predict(X,
                                      return_instance_score=ret_instance_score)

        data = json.loads(json.dumps(ad_preds, cls=NumpyEncoder))
        return ModelResponse(data=data, metrics=None)
예제 #2
0
    def process_event(self, inputs: Union[List, Dict],
                      headers: Dict) -> Optional[ModelResponse]:
        """
        Process the event and return Alibi Detect score

        Parameters
        ----------
        inputs
             Input data
        headers
             Header options

        Returns
        -------
             Alibi Detect response

        """
        logging.info("PROCESSING EVENT.")
        logging.info(str(headers))
        logging.info("----")
        try:
            X = read_inputs_as_numpy(inputs)
        except Exception as e:
            raise Exception(
                "Failed to initialize NumPy array from inputs: %s, %s" %
                (e, inputs))

        ret_instance_score = False
        if (HEADER_RETURN_INSTANCE_SCORE in headers
                and headers[HEADER_RETURN_INSTANCE_SCORE]
                == "true") or RETURN_INSTANCE_SCORE:
            ret_instance_score = True

        outlier_type = "instance"
        if HEADER_OUTLIER_TYPE in headers and headers[HEADER_OUTLIER_TYPE]:
            outlier_type = headers[HEADER_OUTLIER_TYPE]
        ret_feature_score = False
        if (HEADER_RETURN_FEATURE_SCORE in headers
                and headers[HEADER_RETURN_FEATURE_SCORE]
                == "true") or RETURN_FEATURE_SCORE:
            ret_feature_score = True
        od_preds = {}
        name = self.model.meta["name"]
        if (name == "IForest" or name == "OutlierAEGMM"
                or name == "Mahalanobis" or name == "SpectralResidual"
                or name == "OutlierVAEGMM"):
            od_preds = self.model.predict(
                X,
                # scores used to determine outliers
                return_instance_score=ret_instance_score,
            )
        else:
            od_preds = self.model.predict(
                X,
                outlier_type=outlier_type,
                # use 'feature' or 'instance' level
                return_feature_score=ret_feature_score,
                # scores used to determine outliers
                return_instance_score=ret_instance_score,
            )

        # Register metrics
        metrics: List[Dict] = []
        _append_outlier_metrcs(metrics, od_preds, "is_outlier")
        _append_outlier_metrcs(metrics,
                               od_preds,
                               "instance_score",
                               is_count=False)

        # clean result
        if ("data" in od_preds and "instance_score" in od_preds["data"]
                and od_preds["data"]["instance_score"] is None):
            del od_preds["data"]["instance_score"]
        if ("data" in od_preds and "feature_score" in od_preds["data"]
                and od_preds["data"]["feature_score"] is None):
            del od_preds["data"]["feature_score"]

        resp_data = json.loads(json.dumps(od_preds, cls=NumpyEncoder))

        return ModelResponse(data=resp_data, metrics=metrics)
예제 #3
0
    def process_event(self, inputs: Union[List, Dict],
                      headers: Dict) -> Optional[ModelResponse]:
        """
        Process the event and return Alibi Detect score

        Parameters
        ----------
        inputs
             Input data
        headers
             Header options

        Returns
        -------
             Alibi Detect response

        """
        logging.info("PROCESSING EVENT.")
        logging.info(str(headers))
        logging.info("----")
        try:
            X = np.array(inputs)
        except Exception as e:
            raise Exception(
                "Failed to initialize NumPy array from inputs: %s, %s" %
                (e, inputs))

        if self.batch is None:
            self.batch = X
        else:
            self.batch = np.concatenate((self.batch, X))

        self.batch = cast(np.ndarray, self.batch)

        if self.batch.shape[0] >= self.drift_batch_size:
            logging.info(
                "Running drift detection. Batch size is %d. Needed %d",
                self.batch.shape[0],
                self.drift_batch_size,
            )

            cd_preds = self.model.predict(self.batch)

            logging.info("Ran drift test")
            self.batch = None

            output = json.loads(json.dumps(cd_preds, cls=NumpyEncoder))

            metrics: List[Dict] = []
            drift = output.get("data")

            if drift:
                _append_drift_metrcs(metrics, drift, "is_drift")
                _append_drift_metrcs(metrics, drift, "distance")
                _append_drift_metrcs(metrics, drift, "p_val")
                _append_drift_metrcs(metrics, drift, "threshold")

            return ModelResponse(data=output, metrics=metrics)
        else:
            logging.info(
                "Not running drift detection. Batch size is %d. Need %d",
                self.batch.shape[0],
                self.drift_batch_size,
            )
            return None
예제 #4
0
 def getResponse() -> ModelResponse:
     return ModelResponse(data={"foo": 1}, metrics=None)
예제 #5
0
    def process_event(self, inputs: Union[List, Dict], headers: Dict) -> Optional[ModelResponse]:
        """
        Process the event and return Alibi Detect score

        Parameters
        ----------
        inputs
             Input data
        headers
             Header options

        Returns
        -------
             SeldonResponse response

        """
        logging.info("PROCESSING Feedback Event.")
        logging.info(str(headers))
        logging.info("----")

        metrics: List[Dict] = []
        output: Dict = {}
        truth = None
        response = None
        error = None

        if not isinstance(inputs, dict):
            raise SeldonMicroserviceException(
                f"Data is not a dict: {json.dumps(inputs)}",
                status_code=400,
                reason="BAD_DATA",
            )

        if "truth" not in inputs:
            raise SeldonMicroserviceException(
                f"No truth value provided in: {json.dumps(inputs)}",
                status_code=400,
                reason="NO_TRUTH_VALUE",
            )
        else:
            truth = inputs["truth"]

        # We automatically add any metrics provided in the incoming request
        if "metrics" in inputs:
            metrics.extend(inputs["metrics"])

        # If response is provided then we can perform a comparison
        if "response" in inputs:
            response = inputs["response"]
        elif REQUEST_ID_HEADER_NAME in headers:
            # Otherwise if UUID is provided we can fetch from elasticsearch
            if not self.elasticsearch_client:
                error = "Seldon-Puid provided but elasticsearch client not configured"
            else:
                try:
                    seldon_puid = headers.get(REQUEST_ID_HEADER_NAME, "")
                    seldon_namespace = headers.get(NAMESPACE_HEADER_NAME, "")

                    # Currently only supports SELDON inference type (not kfserving)
                    elasticsearch_index = f"inference-log-seldon-{seldon_namespace}-{SELDON_DEPLOYMENT_ID}-{SELDON_PREDICTOR_ID}"

                    doc = self.elasticsearch_client.get(
                        index=elasticsearch_index, id=seldon_puid
                    )
                    response = (
                        doc.get("_source", {})
                        .get("response", None)
                        .get("instance", None)
                    )
                    if not response:
                        error = f"Elasticsearch index {elasticsearch_index} with id {seldon_puid} did not contain response value"
                except NotFoundError:
                    error = f"Elasticsearch index {elasticsearch_index} with id {seldon_puid} not found"
        else:
            error = "Neither response nor request Puid provided in headers"

        if error:
            raise SeldonMicroserviceException(
                error, status_code=400, reason="METRICS_SERVER_ERROR"
            )

        logging.error(f"{truth}, {response}")
        metrics_transformed = self.model.transform(truth, response)

        metrics.extend(metrics_transformed.metrics)

        return ModelResponse(data={}, metrics=metrics)