Spaces:
Runtime error
Runtime error
Download inference.py from SamuelTheo/soft-computing-ai: direct link, hf CLI and curl.
- Browser
- Download file 7.7 kB
-
https://huggingface.co/spaces/SamuelTheo/soft-computing-ai/resolve/main/inference.py
- Command line
-
hf download hf://spaces/SamuelTheo/soft-computing-ai/inference.py
-
curl -L -o inference.py https://huggingface.co/spaces/SamuelTheo/soft-computing-ai/resolve/main/inference.py
7.7 kB
| from __future__ import annotations | |
| from functools import lru_cache | |
| from pathlib import Path | |
| from typing import Any | |
| import cv2 | |
| import numpy as np | |
| import skfuzzy as fuzz | |
| import tensorflow as tf | |
| from skfuzzy import control as ctrl | |
| IMAGE_SIZE = (256, 256) | |
| LABEL_TOKENS = ("neg", "neutral", "pos") | |
| LABEL_MAP = {label: index for index, label in enumerate(LABEL_TOKENS)} | |
| LABEL_NAMES = { | |
| LABEL_MAP["neg"]: "Negative", | |
| LABEL_MAP["neutral"]: "Neutral", | |
| LABEL_MAP["pos"]: "Positive", | |
| } | |
| NUM_CLASSES = len(LABEL_TOKENS) | |
| MODEL_PATH = ( | |
| Path(__file__).resolve().parent / "artifacts" / "cnn_fis_classifier.keras" | |
| ) | |
| def get_model_path() -> Path: | |
| return MODEL_PATH.resolve() | |
| def load_model(): | |
| model_path = get_model_path() | |
| if not model_path.exists(): | |
| raise FileNotFoundError( | |
| f"Keras model tidak ditemukan di: {model_path}. " | |
| "Pastikan file artefak ada di folder ai_engine/artifacts." | |
| ) | |
| return tf.keras.models.load_model(model_path) | |
| def preprocess_image_bytes(image_bytes: bytes) -> np.ndarray: | |
| image_array = np.frombuffer(image_bytes, dtype=np.uint8) | |
| image = cv2.imdecode(image_array, cv2.IMREAD_COLOR) | |
| if image is None: | |
| raise ValueError("Gambar tidak bisa dibaca. Pastikan file upload adalah gambar yang valid.") | |
| image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
| image = cv2.resize(image, IMAGE_SIZE, interpolation=cv2.INTER_AREA) | |
| image = image.astype(np.float32) / 255.0 | |
| return image | |
| def normalize_probability_vector(model_output: np.ndarray) -> np.ndarray: | |
| probabilities = np.asarray(model_output, dtype=np.float32).reshape(-1) | |
| if probabilities.shape[0] == NUM_CLASSES: | |
| return probabilities | |
| raise ValueError( | |
| f"Expected {NUM_CLASSES} class probabilities for " | |
| f"{', '.join(LABEL_TOKENS)}, received shape {probabilities.shape}." | |
| ) | |
| def probability_dict_from_vector(probability_vector: np.ndarray) -> dict[str, float]: | |
| probabilities = normalize_probability_vector(probability_vector) | |
| if probabilities.shape[0] != NUM_CLASSES: | |
| raise ValueError( | |
| f"Expected {NUM_CLASSES} class probabilities, received shape {probabilities.shape}." | |
| ) | |
| return { | |
| "negative": float(probabilities[LABEL_MAP["neg"]]), | |
| "neutral": float(probabilities[LABEL_MAP["neutral"]]), | |
| "positive": float(probabilities[LABEL_MAP["pos"]]), | |
| } | |
| def build_fuzzy_system() -> ctrl.ControlSystemSimulation: | |
| positive_confidence = ctrl.Antecedent(np.linspace(0, 1, 101), "positive_confidence") | |
| neutral_confidence = ctrl.Antecedent(np.linspace(0, 1, 101), "neutral_confidence") | |
| negative_confidence = ctrl.Antecedent(np.linspace(0, 1, 101), "negative_confidence") | |
| final_decision = ctrl.Consequent(np.linspace(0, 100, 101), "final_decision") | |
| for variable in (positive_confidence, neutral_confidence, negative_confidence): | |
| variable["low"] = fuzz.trimf(variable.universe, [0.0, 0.0, 0.45]) | |
| variable["medium"] = fuzz.trimf(variable.universe, [0.2, 0.5, 0.8]) | |
| variable["high"] = fuzz.trimf(variable.universe, [0.55, 1.0, 1.0]) | |
| final_decision["negative"] = fuzz.trimf(final_decision.universe, [0, 0, 35]) | |
| final_decision["neutral"] = fuzz.trimf(final_decision.universe, [30, 50, 70]) | |
| final_decision["positive"] = fuzz.trimf(final_decision.universe, [65, 100, 100]) | |
| rules = [ | |
| ctrl.Rule( | |
| positive_confidence["high"] | |
| & neutral_confidence["low"] | |
| & negative_confidence["low"], | |
| final_decision["positive"], | |
| ), | |
| ctrl.Rule( | |
| positive_confidence["high"] | |
| & neutral_confidence["medium"] | |
| & negative_confidence["low"], | |
| final_decision["positive"], | |
| ), | |
| ctrl.Rule( | |
| positive_confidence["medium"] | |
| & neutral_confidence["low"] | |
| & negative_confidence["low"], | |
| final_decision["positive"], | |
| ), | |
| ctrl.Rule( | |
| negative_confidence["high"] | |
| & positive_confidence["low"] | |
| & neutral_confidence["low"], | |
| final_decision["negative"], | |
| ), | |
| ctrl.Rule( | |
| negative_confidence["high"] | |
| & positive_confidence["low"] | |
| & neutral_confidence["medium"], | |
| final_decision["negative"], | |
| ), | |
| ctrl.Rule( | |
| negative_confidence["medium"] | |
| & positive_confidence["low"] | |
| & neutral_confidence["low"], | |
| final_decision["negative"], | |
| ), | |
| ctrl.Rule( | |
| neutral_confidence["high"] | |
| & positive_confidence["low"] | |
| & negative_confidence["low"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| neutral_confidence["high"] | |
| & positive_confidence["medium"] | |
| & negative_confidence["low"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| neutral_confidence["high"] | |
| & negative_confidence["medium"] | |
| & positive_confidence["low"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| positive_confidence["medium"] & negative_confidence["medium"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| positive_confidence["medium"] & neutral_confidence["medium"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| negative_confidence["medium"] & neutral_confidence["medium"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| positive_confidence["high"] & negative_confidence["high"], | |
| final_decision["neutral"], | |
| ), | |
| ctrl.Rule( | |
| positive_confidence["low"] | |
| & negative_confidence["low"] | |
| & neutral_confidence["low"], | |
| final_decision["neutral"], | |
| ), | |
| ] | |
| control_system = ctrl.ControlSystem(rules) | |
| return ctrl.ControlSystemSimulation(control_system) | |
| def fuzzy_label_from_probabilities(probability_vector: np.ndarray) -> tuple[str, float]: | |
| probabilities = probability_dict_from_vector(probability_vector) | |
| fuzzy_system = build_fuzzy_system() | |
| fuzzy_system.input["positive_confidence"] = probabilities["positive"] | |
| fuzzy_system.input["neutral_confidence"] = probabilities["neutral"] | |
| fuzzy_system.input["negative_confidence"] = probabilities["negative"] | |
| fuzzy_system.compute() | |
| decision_score = float(fuzzy_system.output["final_decision"]) | |
| if decision_score >= 65: | |
| label = "Positive" | |
| elif decision_score <= 35: | |
| label = "Negative" | |
| else: | |
| label = "Neutral" | |
| return label, decision_score | |
| def predict_image_bytes(image_bytes: bytes) -> dict[str, Any]: | |
| image = preprocess_image_bytes(image_bytes) | |
| model = load_model() | |
| batch = np.expand_dims(image, axis=0).astype(np.float32) | |
| model_output = np.asarray(model.predict(batch, verbose=0)[0], dtype=np.float32) | |
| probability_vector = normalize_probability_vector(model_output) | |
| probabilities = probability_dict_from_vector(probability_vector) | |
| fuzzy_label, fuzzy_score = fuzzy_label_from_probabilities(probability_vector) | |
| cnn_prediction_name = LABEL_NAMES[int(np.argmax(probability_vector))] | |
| return { | |
| "label": fuzzy_label, | |
| "fis_score": fuzzy_score, | |
| "cnn_prediction_name": cnn_prediction_name, | |
| "probabilities": probabilities, | |
| } | |