Compartir vía


Tutorial: creación, evaluación y puntuación de un modelo de detección de errores de máquina

En este tutorial se muestra un ejemplo completo de un flujo de trabajo de ciencia de datos de Synapse en Microsoft Fabric. En el escenario se usa el aprendizaje automático para tener un enfoque más sistemático del diagnóstico de errores con el fin de identificar de forma proactiva las incidencias y tomar medidas antes de que se produzca un error real de una máquina. El objetivo es predecir si una máquina experimentaría un error en función de la temperatura del proceso, la velocidad de rotación, etc.

En este tutorial se describen estos pasos:

  • Instalación de bibliotecas personalizadas
  • Cargar y procesar los datos
  • Comprender los datos a través de un análisis exploratorio de los datos
  • Usar scikit-learn, LightGBM y MLflow para entrenar modelos de Machine Learning y usar la característica de registro automático de Fabric para realizar un seguimiento de los experimentos
  • Puntuar los modelos entrenados con la característica PREDICT de Fabric, guardar el mejor modelo y cargarlo para las predicciones
  • Mostrar el rendimiento del modelo cargado con visualizaciones de Power BI

Requisitos previos

Seguir en un cuaderno

Puede elegir una de estas opciones para seguir en un cuaderno:

  • Abra y ejecute el cuaderno integrado en la experiencia de ciencia de datos
  • Cargue su cuaderno desde GitHub a la experiencia de ciencia de datos

Abra el cuaderno integrado

El cuaderno de muestra Machine failure acompaña a este tutorial.

Para abrir el cuaderno de muestra integrado en el tutorial en la experiencia de ciencia de datos de Synapse:

  1. Vaya a la página principal de ciencia de datos de Synapse.

  2. Seleccione Utilizar una muestra.

  3. Seleccione la muestra correspondiente:

    • Desde la pestaña predeterminada Flujos de trabajo de un extremo a otro (Python), si la muestra es para un tutorial de Python.
    • Desde la pestaña Flujos de trabajo de un extremo a otro (R), si la muestra es para un tutorial de R.
    • En la pestaña Tutoriales rápidos, si la muestra es para un tutorial rápido.
  4. Adjunte una instancia de LakeHouse al cuaderno antes de empezar a ejecutar código.

Importación del cuaderno desde GitHub

El cuaderno AISample - Predictive Maintenance acompaña a este tutorial.

Para abrir el cuaderno complementario para este tutorial, siga las instrucciones en Preparación del sistema para los tutoriales de ciencia de datos para importar el cuaderno en el área de trabajo.

Si prefiere copiar y pegar el código de esta página, puede crear un cuaderno nuevo.

Asegúrese de adjuntar una instancia de LakeHouse al cuaderno antes de empezar a ejecutar código.

Paso 1: Instalación de bibliotecas personalizadas

Para desarrollar modelos de Machine Learning o realizar análisis de datos ad hoc, es posible que tenga que instalar rápidamente una biblioteca personalizada para la sesión de Apache Spark. Tiene dos opciones para instalar bibliotecas.

  • Use las funcionalidades de instalación insertadas (%pip o %conda) del cuaderno para instalar una biblioteca solo en el cuaderno actual.
  • Como alternativa, puede crear un entorno de Fabric, instalar bibliotecas desde orígenes públicos o cargar bibliotecas personalizadas en él y, después, el administrador del área de trabajo puede asociar el entorno como valor predeterminado para el área de trabajo. Todas las bibliotecas del entorno estarán disponibles para su uso en los cuadernos y las definiciones de trabajo de Spark del área de trabajo. Para obtener más información sobre los entornos, consulte Creación, configuración y uso de un entorno en Microsoft Fabric.

En este tutorial, usará %pip install para instalar la biblioteca imblearn en el cuaderno.

Nota:

El kernel de PySpark se reiniciará después de %pip install ejecuciones. Si lo necesita, instale bibliotecas antes de ejecutar cualquier otra celda.

# Use pip to install imblearn
%pip install imblearn

Paso 2: Carga de los datos

El conjunto de datos simula el registro de los parámetros de una máquina de fabricación como una función de tiempo, que es común en la configuración industrial. Consta de 10 000 puntos de datos almacenados como filas con características como columnas. Estas características incluyen lo siguiente:

  • Un identificador único (UID) comprendido entre 1 y 10 000

  • Id. del producto, que consta de una letra L (para baja), M (para media) o H (para alta), que indica la variante de calidad del producto y un número de serie específico de variante. Las variantes de baja, media y alta calidad constituyen un 60 %, un 30 % y un 10 % de todos los productos, respectivamente

  • Temperatura del aire, en grados Kelvin (K)

  • Temperatura del proceso, en grados Kelvin

  • Velocidad rotacional, en revoluciones por minuto (RPM)

  • Par motor, en metros Newton (Nm)

  • Desgaste de la herramienta, en minutos. Las variantes de calidad H, M y L agregan 5, 3 y 2 minutos de desgaste de herramienta respectivamente a la herramienta usada en el proceso

  • Una etiqueta de error de la máquina, para indicar si la máquina produjo un error en el punto de datos específico. Este punto de datos específico puede tener cualquiera de los cinco modos de error independientes siguientes:

    • Error de desgaste de la herramienta (TWF): la herramienta se reemplaza o produce un error en un tiempo de desgaste de herramienta seleccionado aleatoriamente entre 200 y 240 minutos
    • Error de disipación de calor (HDF): la disipación de calor provoca un error de proceso si la diferencia entre la temperatura del aire y la temperatura del proceso es inferior a 8,6 K y la velocidad rotacional de la herramienta está por debajo de 1380 RPM.
    • Error de alimentación (PWF): el producto del par motor y la velocidad de rotación (en rad/s) es igual a la potencia necesaria para el proceso. El proceso produce un error si esta potencia baja de 3500 W o supera 9000 W
    • Error de sobreesfuerzo (OSF): si el producto del desgaste de la herramienta y el par motor supera los 11 000 Nm mínimos para la variante del producto L (12 000 para M y 13 000 para H), el proceso produce un error debido a sobreesfuerzo
    • Errores aleatorios (RNF): cada proceso tiene una posibilidad de error del 0,1 % independientemente de sus parámetros de proceso

Nota:

Si al menos uno de los modos de error anteriores es true, se produce un error en el proceso y la etiqueta "error de la máquina" se establece en 1. El método de aprendizaje automático no puede determinar qué modo de error provocó el error del proceso.

Descarga del conjunto de datos y carga en un almacén de lago de datos

Conéctese al contenedor de Azure Open Datasets y cargue el conjunto de datos de mantenimiento predictivo. El código siguiente descarga una versión disponible públicamente del conjunto de datos y, a continuación, la almacena en un almacén de lago de Fabric:

Importante

Agregue un almacén de lago al cuaderno antes de ejecutarlo. De lo contrario, recibirá un error. Para obtener información sobre cómo agregar un almacén de lago, consulte Conexión de almacenes de lago y cuadernos.

# Download demo data files into the lakehouse if they don't exist
import os, requests
DATA_FOLDER = "Files/predictive_maintenance/"  # Folder that contains the dataset
DATA_FILE = "predictive_maintenance.csv"  # Data file name
remote_url = "https://synapseaisolutionsa.blob.core.windows.net/public/MachineFaultDetection"
file_list = ["predictive_maintenance.csv"]
download_path = f"/lakehouse/default/{DATA_FOLDER}/raw"

if not os.path.exists("/lakehouse/default"):
    raise FileNotFoundError(
        "Default lakehouse not found, please add a lakehouse and restart the session."
    )
os.makedirs(download_path, exist_ok=True)
for fname in file_list:
    if not os.path.exists(f"{download_path}/{fname}"):
        r = requests.get(f"{remote_url}/{fname}", timeout=30)
        with open(f"{download_path}/{fname}", "wb") as f:
            f.write(r.content)
print("Downloaded demo data files into lakehouse.")

Después de descargar el conjunto de datos en el almacén de lago, puede cargarlo como DataFrame de Spark:

df = (
    spark.read.option("header", True)
    .option("inferSchema", True)
    .csv(f"{DATA_FOLDER}raw/{DATA_FILE}")
    .cache()
)
df.show(5)

En esta tabla se muestra una vista previa de los datos:

UDI Product ID Tipo Temperatura del aire [K] Temperatura del proceso [K] Velocidad de rotación [rpm] Par motor [Nm] Desgaste de la herramienta [min] Destino Tipo del error
1 M14860 M 298,1 308,6 1 551 42,8 0 0 No hay ningún error
2 L47181 L 298,2 308,7 1408 46,3 3 0 No hay ningún error
3 L47182 L 298,1 308,5 1498 49,4 5 0 No hay ningún error
4 L47183 L 298,2 308,6 1433 39,5 7 0 No hay ningún error
5 L47184 L 298,2 308,7 1408 40,0 9 0 No hay ningún error

Escritura de un DataFrame de Spark en una tabla delta de almacén de lago

Dé formato a los datos (por ejemplo, reemplace los espacios por un carácter de subrayado) para facilitar las operaciones de Spark en los pasos posteriores:

# Replace the space in the column name with an underscore to avoid an invalid character while saving 
df = df.toDF(*(c.replace(' ', '_') for c in df.columns))
table_name = "predictive_maintenance_data"
df.show(5)

En esta tabla se muestra una vista previa de los datos con nombres de columna con nuevo formato:

UDI Product_ID Tipo Air_temperature_[K] Process_temperature_[K] Rotational_speed_[rpm] Torque_[Nm] Tool_wear_[min] Destino Failure_Type
1 M14860 M 298,1 308,6 1 551 42,8 0 0 No hay ningún error
2 L47181 L 298,2 308,7 1408 46,3 3 0 No hay ningún error
3 L47182 L 298,1 308,5 1498 49,4 5 0 No hay ningún error
4 L47183 L 298,2 308,6 1433 39,5 7 0 No hay ningún error
5 L47184 L 298,2 308,7 1408 40,0 9 0 No hay ningún error
# Save data with processed columns to the lakehouse 
df.write.mode("overwrite").format("delta").save(f"Tables/{table_name}")
print(f"Spark DataFrame saved to delta table: {table_name}")

Paso 3: Preprocesamiento y realización de análisis exploratorios de datos

Convierta el DataFrame de Spark en un DataFrame de Pandas para usar bibliotecas de trazado populares compatibles con Pandas.

Sugerencia

En el caso de los conjuntos de datos grandes, es posible que tenga que cargar una parte del conjunto de datos.

data = spark.read.format("delta").load("Tables/predictive_maintenance_data")
SEED = 1234
df = data.toPandas()
df.drop(['UDI', 'Product_ID'],axis=1,inplace=True)
# Rename the Target column to IsFail
df = df.rename(columns = {'Target': "IsFail"})
df.info()

Convierta columnas específicas del conjunto de datos en tipos flotantes o enteros según sea necesario, y asigne cadenas ('L', 'M' y 'H') a valores numéricos (0, 1 y 2):

# Convert temperature, rotational speed, torque, and tool wear columns to float
df['Air_temperature_[K]'] = df['Air_temperature_[K]'].astype(float)
df['Process_temperature_[K]'] = df['Process_temperature_[K]'].astype(float)
df['Rotational_speed_[rpm]'] = df['Rotational_speed_[rpm]'].astype(float)
df['Torque_[Nm]'] = df['Torque_[Nm]'].astype(float)
df['Tool_wear_[min]'] = df['Tool_wear_[min]'].astype(float)

# Convert the 'Target' column to an integer 
df['IsFail'] = df['IsFail'].astype(int)
# Map 'L', 'M', 'H' to numerical values 
df['Type'] = df['Type'].map({'L': 0, 'M': 1, 'H': 2})

Exploración de datos a través de visualizaciones

# Import packages and set plotting style
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
sns.set_style('darkgrid')

# Create the correlation matrix
corr_matrix = df.corr(numeric_only=True)

# Plot a heatmap
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True)
plt.show()

Screenshot showing a plot of the correlation matrix of features.

Como se esperaba, el error (IsFail) tiene correlación con las características seleccionadas (columnas). La matriz de correlación muestra que Air_temperature, Process_temperature, Rotational_speed, Torque y Tool_wear tienen la correlación más alta con la variable IsFail.

# Plot histograms of select features
fig, axes = plt.subplots(2, 3, figsize=(18,10))
columns = ['Air_temperature_[K]', 'Process_temperature_[K]', 'Rotational_speed_[rpm]', 'Torque_[Nm]', 'Tool_wear_[min]']
data=df.copy()
for ind, item in enumerate (columns):
    column = columns[ind]
    df_column = data[column]
    df_column.hist(ax = axes[ind%2][ind//2], bins=32).set_title(item)
fig.supylabel('count')
fig.subplots_adjust(hspace=0.2)
fig.delaxes(axes[1,2])

Screenshot showing a graph plot of the features.

Como se muestra en los gráficos trazados, las variables Air_temperature, Process_temperature, Rotational_speed, Torque y Tool_wear no son dispersas. Parecen tener una buena continuidad en el espacio de características. Estos trazados confirman que el entrenamiento de un modelo de Machine Learning en este conjunto de datos es probable que genere resultados fiables que se puedan generalizar en un nuevo conjunto de datos.

Inspección de la variable de destino para el desequilibrio de clases

Cuente el número de muestras de las máquinas con errores y sin errores e inspeccione el equilibrio de datos de cada clase (IsFail=0, IsFail=1):

# Plot the counts for no failure and each failure type
plt.figure(figsize=(12, 2))
ax = sns.countplot(x='Failure_Type', data=df)
for p in ax.patches:
    ax.annotate(f'{p.get_height()}', (p.get_x()+0.4, p.get_height()+50))

plt.show()

# Plot the counts for no failure versus the sum of all failure types
plt.figure(figsize=(4, 2))
ax = sns.countplot(x='IsFail', data=df)
for p in ax.patches:
    ax.annotate(f'{p.get_height()}', (p.get_x()+0.4, p.get_height()+50))

plt.show()

Screenshot of a plot showing that samples are imbalanced.

Los trazados indican que la clase sin error (mostrada como IsFail=0 en el segundo trazado) constituye la mayoría de las muestras. Use una técnica de sobremuestreo para crear un conjunto de datos de entrenamiento más equilibrado:

# Separate features and target
features = df[['Type', 'Air_temperature_[K]', 'Process_temperature_[K]', 'Rotational_speed_[rpm]', 'Torque_[Nm]', 'Tool_wear_[min]']]
labels = df['IsFail']

# Split the dataset into the training and testing sets
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)

# Ignore warnings
import warnings
warnings.filterwarnings('ignore')
# Save test data to the lakehouse for use in future sections
table_name = "predictive_maintenance_test_data"
df_test_X = spark.createDataFrame(X_test)
df_test_X.write.mode("overwrite").format("delta").save(f"Tables/{table_name}")
print(f"Spark DataFrame saved to delta table: {table_name}")

Sobremuestreo para equilibrar las clases del conjunto de datos de entrenamiento

El análisis anterior mostró que el conjunto de datos está muy desequilibrado. El desequilibrio para a ser un problema porque la clase que es minoría tiene demasiado pocos para que el modelo aprenda eficazmente el límite de decisión.

SMOTE puede resolver el problema. SMOTE es una técnica de sobremuestreo ampliamente utilizada que genera ejemplos sintéticos. Genera ejemplos para la clase que es minoría en función de las distancias euclidianas entre los puntos de datos. Este método difiere del sobremuestreo aleatorio, ya que crea nuevos ejemplos que no solo duplican la clase que es minoría. El método se convierte en una técnica más eficaz para controlar conjuntos de datos desequilibrados.

# Disable MLflow autologging because you don't want to track SMOTE fitting
import mlflow

mlflow.autolog(disable=True)

from imblearn.combine import SMOTETomek
smt = SMOTETomek(random_state=SEED)
X_train_res, y_train_res = smt.fit_resample(X_train, y_train)

# Plot the counts for both classes
plt.figure(figsize=(4, 2))
ax = sns.countplot(x='IsFail', data=pd.DataFrame({'IsFail': y_train_res.values}))
for p in ax.patches:
    ax.annotate(f'{p.get_height()}', (p.get_x()+0.4, p.get_height()+50))

plt.show()

Screenshot of a plot showing that samples are balanced.

Ha equilibrado correctamente el conjunto de datos. Ahora puede pasar al entrenamiento del modelo.

Paso 4: Entrenamiento y evaluación de los modelos

MLflow registra modelos, entrena y compara varios modelos, y elige el mejor modelo con fines de predicción. Puede usar los tres modelos siguientes para el entrenamiento de modelos:

  • Clasificador de bosque aleatorio
  • Clasificador de regresión logística
  • Clasificador de XGBoost

Entrenamiento de un clasificador de bosque aleatorio

import numpy as np 
from sklearn.ensemble import RandomForestClassifier
from mlflow.models.signature import infer_signature
from sklearn.metrics import f1_score, accuracy_score, recall_score

mlflow.set_experiment("Machine_Failure_Classification")
mlflow.autolog(exclusive=False) # This is needed to override the preconfigured autologging behavior

with mlflow.start_run() as run:
    rfc_id = run.info.run_id
    print(f"run_id {rfc_id}, status: {run.info.status}")
    rfc = RandomForestClassifier(max_depth=5, n_estimators=50)
    rfc.fit(X_train_res, y_train_res) 
    signature = infer_signature(X_train_res, y_train_res)

    mlflow.sklearn.log_model(
        rfc,
        "machine_failure_model_rf",
        signature=signature,
        registered_model_name="machine_failure_model_rf"
    ) 

    y_pred_train = rfc.predict(X_train)
    # Calculate the classification metrics for test data
    f1_train = f1_score(y_train, y_pred_train, average='weighted')
    accuracy_train = accuracy_score(y_train, y_pred_train)
    recall_train = recall_score(y_train, y_pred_train, average='weighted')

    # Log the classification metrics to MLflow
    mlflow.log_metric("f1_score_train", f1_train)
    mlflow.log_metric("accuracy_train", accuracy_train)
    mlflow.log_metric("recall_train", recall_train)

    # Print the run ID and the classification metrics
    print("F1 score_train:", f1_train)
    print("Accuracy_train:", accuracy_train)
    print("Recall_train:", recall_train)    

    y_pred_test = rfc.predict(X_test)
    # Calculate the classification metrics for test data
    f1_test = f1_score(y_test, y_pred_test, average='weighted')
    accuracy_test = accuracy_score(y_test, y_pred_test)
    recall_test = recall_score(y_test, y_pred_test, average='weighted')

    # Log the classification metrics to MLflow
    mlflow.log_metric("f1_score_test", f1_test)
    mlflow.log_metric("accuracy_test", accuracy_test)
    mlflow.log_metric("recall_test", recall_test)

    # Print the classification metrics
    print("F1 score_test:", f1_test)
    print("Accuracy_test:", accuracy_test)
    print("Recall_test:", recall_test)

A partir de la salida, tanto el conjunto de datos de entrenamiento como el de prueba producen una puntuación F1, precisión y coincidencia de aproximadamente 0,9 mediante el clasificador de bosque aleatorio.

Entrenamiento de un modelo de regresión logística

from sklearn.linear_model import LogisticRegression

with mlflow.start_run() as run:
    lr_id = run.info.run_id
    print(f"run_id {lr_id}, status: {run.info.status}")
    lr = LogisticRegression(random_state=42)
    lr.fit(X_train_res, y_train_res)
    signature = infer_signature(X_train_res, y_train_res)
  
    mlflow.sklearn.log_model(
        lr,
        "machine_failure_model_lr",
        signature=signature,
        registered_model_name="machine_failure_model_lr"
    ) 

    y_pred_train = lr.predict(X_train)
    # Calculate the classification metrics for training data
    f1_train = f1_score(y_train, y_pred_train, average='weighted')
    accuracy_train = accuracy_score(y_train, y_pred_train)
    recall_train = recall_score(y_train, y_pred_train, average='weighted')

    # Log the classification metrics to MLflow
    mlflow.log_metric("f1_score_train", f1_train)
    mlflow.log_metric("accuracy_train", accuracy_train)
    mlflow.log_metric("recall_train", recall_train)

    # Print the run ID and the classification metrics
    print("F1 score_train:", f1_train)
    print("Accuracy_train:", accuracy_train)
    print("Recall_train:", recall_train)    

    y_pred_test = lr.predict(X_test)
    # Calculate the classification metrics for test data
    f1_test = f1_score(y_test, y_pred_test, average='weighted')
    accuracy_test = accuracy_score(y_test, y_pred_test)
    recall_test = recall_score(y_test, y_pred_test, average='weighted')

    # Log the classification metrics to MLflow
    mlflow.log_metric("f1_score_test", f1_test)
    mlflow.log_metric("accuracy_test", accuracy_test)
    mlflow.log_metric("recall_test", recall_test)

Entrenamiento de un clasificador de XGBoost

from xgboost import XGBClassifier

with mlflow.start_run() as run:
    xgb = XGBClassifier()
    xgb_id = run.info.run_id 
    print(f"run_id {xgb_id}, status: {run.info.status}")
    xgb.fit(X_train_res.to_numpy(), y_train_res.to_numpy()) 
    signature = infer_signature(X_train_res, y_train_res)
  
    mlflow.xgboost.log_model(
        xgb,
        "machine_failure_model_xgb",
        signature=signature,
        registered_model_name="machine_failure_model_xgb"
    ) 

    y_pred_train = xgb.predict(X_train)
    # Calculate the classification metrics for training data
    f1_train = f1_score(y_train, y_pred_train, average='weighted')
    accuracy_train = accuracy_score(y_train, y_pred_train)
    recall_train = recall_score(y_train, y_pred_train, average='weighted')

    # Log the classification metrics to MLflow
    mlflow.log_metric("f1_score_train", f1_train)
    mlflow.log_metric("accuracy_train", accuracy_train)
    mlflow.log_metric("recall_train", recall_train)

    # Print the run ID and the classification metrics
    print("F1 score_train:", f1_train)
    print("Accuracy_train:", accuracy_train)
    print("Recall_train:", recall_train)    

    y_pred_test = xgb.predict(X_test)
    # Calculate the classification metrics for test data
    f1_test = f1_score(y_test, y_pred_test, average='weighted')
    accuracy_test = accuracy_score(y_test, y_pred_test)
    recall_test = recall_score(y_test, y_pred_test, average='weighted')

    # Log the classification metrics to MLflow
    mlflow.log_metric("f1_score_test", f1_test)
    mlflow.log_metric("accuracy_test", accuracy_test)
    mlflow.log_metric("recall_test", recall_test)

Paso 5: Selección del mejor modelo y predicción de salidas

En la sección anterior, ha entrenado tres clasificadores diferentes: bosque aleatorio, regresión logística y XGBoost. Ahora tiene la opción de acceder mediante programación a los resultados o usar la interfaz de usuario (UI).

Para usar la opción de ruta de acceso de la UI, vaya al área de trabajo y filtre los modelos.

Screenshot of the filter, with models selected.

Seleccione modelos individuales para obtener más información sobre el rendimiento del modelo.

Screenshot of performance details for models.

En este ejemplo se muestra cómo acceder mediante programación a los modelos a través de MLflow:

runs = {'random forest classifier':   rfc_id,
        'logistic regression classifier': lr_id,
        'xgboost classifier': xgb_id}

# Create an empty DataFrame to hold the metrics
df_metrics = pd.DataFrame()

# Loop through the run IDs and retrieve the metrics for each run
for run_name, run_id in runs.items():
    metrics = mlflow.get_run(run_id).data.metrics
    metrics["run_name"] = run_name
    df_metrics = df_metrics.append(metrics, ignore_index=True)

# Print the DataFrame
print(df_metrics)

Aunque XGBoost produce los mejores resultados en el conjunto de entrenamiento, funciona de forma deficiente en el conjunto de datos de prueba. Este rendimiento deficiente indica un sobreajuste. El clasificador de regresión logística funciona de forma deficiente tanto en conjuntos de datos de entrenamiento como de prueba. En general, el bosque aleatorio alcanza un buen equilibrio entre el rendimiento de entrenamiento y evitar el sobreajuste.

En la sección siguiente, elija el modelo de bosque aleatorio registrado y realice una predicción mediante la característica PREDICT:

from synapse.ml.predict import MLFlowTransformer

model = MLFlowTransformer(
    inputCols=list(X_test.columns),
    outputCol='predictions',
    modelName='machine_failure_model_rf',
    modelVersion=1
)

Con el objeto MLFlowTransformer que ha creado para cargar el modelo para la inferencia, use la API de Transformer para puntuar el modelo con el conjunto de datos de prueba:

predictions = model.transform(spark.createDataFrame(X_test))
predictions.show()

En esta tabla se muestra la salida:

Tipo Air_temperature_[K] Process_temperature_[K] Rotational_speed_[rpm] Torque_[Nm] Tool_wear_[min] predicciones
0 300,6 309,7 1639,0 30,4 121,0 0
0 303,9 313,0 1551,0 36,8 140.0 0
1 299,1 308,6 1491,0 38,5 166,0 0
0 300,9 312,1 1359,0 51,7 146,0 1
0 303,7 312,6 1621,0 38,8 182,0 0
0 299,0 310,3 1868,0 24,0 221,0 1
2 297,8 307,5 1631,0 31.3 124,0 0
0 297,5 308,2 1327,0 56,5 189,0 1
0 301,3 310,3 1460,0 41,5 197,0 0
2 297,6 309,0 1413,0 40,2 51,0 0
1 300,9 309,4 1724,0 25,6 119,0 0
0 303,3 311,3 1389,0 53,9 39,0 0
0 298,4 307,9 1981,0 23,2 16.0 0
0 299,3 308,8 1636,0 29,9 201,0 0
1 298,1 309,2 1460,0 45.8 80.0 0
0 300,0 309,5 1728,0 26,0 37,0 0
2 299,0 308,7 1940,0 19,9 98,0 0
0 302,2 310,8 1383,0 46,9 45,0 0
0 300,2 309,2 1431,0 51,3 57,0 0
0 299,6 310,2 1468,0 48,0 9.0 0

Guarde los datos en el almacén de lago. A continuación, los datos estarán disponibles para usos posteriores; por ejemplo, un panel de Power BI.

# Save test data to the lakehouse for use in the next section. 
table_name = "predictive_maintenance_test_with_predictions"
predictions.write.mode("overwrite").format("delta").save(f"Tables/{table_name}")
print(f"Spark DataFrame saved to delta table: {table_name}")

Paso 6: Visualización de la inteligencia empresarial a través de visualizaciones en Power BI

Mostrar los resultados en un formato sin conexión, con un panel de Power BI.

Screenshot of the data displayed as a Power BI dashboard.

El panel muestra que Tool_wear y Torque crean un límite notable entre los casos con errores y sin error, como se esperaba en el análisis de correlación anterior en el paso 2.