home
navigate_next
Blog
navigate_next
Tutorials

How to monitor the performance of a fine-tuned GPT-Neo Model

How to monitor the performance of a fine-tuned GPT-Neo Model
Michael Louis
Co-Founder & CEO

Due to the high cost, latency, and rate limit issues associated with OpenAI, many of our customers have opted to fine-tune and deploy their own models and integrate them into their own applications. However, determining the adequacy of the output generated by these models, whether they are developed by OpenAI or by our customers, as well as identifying the prompts or edge cases that may lead to poor performance, can be a challenging task. To address this challenge, I will show you a reliable method for monitoring model performance using Arize.

Starting today, Cerebrium is announcing its integration with Arize, a leading ML observability platform used by the likes of Instacart, Uber and many more! Arize has many great features such as performance tracing, dashboards, drift analysis and many more that make it a favourite tool. We are going to make use of the embeddings UMAP visualization that Arize offers in order to determine model degradation of our fine-tuned GPT Neo model.

Vector embeddings, are lists of numbers, that represent data such as text in order to perform various operations. Most commonly, vector embeddings are used in recommendations engines and voice assistants.

In this tutorial we are going to follow on from our previous tutorial here, fine-tuning a GPT Neo model on a Netflix dataset to help generate description of movies. After developing the model, we will use Cerebrium for deployment and Arize to monitor the performance of our deployed model. Arize will identify issues with our unstructured data model so we can improve performance .

Tutorial

Fine-tune Model

To get started, fine-tune a GPT Neo model on the Netflix dataset by following the instructions from our previous tutorial here.

Create baseline mode in Arize

To compare changes, perform analysis, and root cause performance degradations, your model needs a model baseline. A model baseline is a reference data set used to compare your current data with either training, validation, or prior time periods in production. Here, we log our descriptions, both the raw text data and the vector embedding representation to Arize and say whether it was a positive or negative generation. We will do this randomly in order to create material for this tutorial. You will see we give every generated description a positive class (represented by 1) meaning this is a good generation and randomly assign if it was actually a good generation or not. You would typically build this binary classification into your application to get feedback from the user.


import random

import pandas as pd
from arize.pandas.logger import Client
from arize.utils.types import ModelTypes, Environments
# Use HF model to do encoding
from sentence_transformers import SentenceTransformer

API_KEY = ''
SPACE_KEY = ''
arize_client = Client(space_key=SPACE_KEY, api_key=API_KEY)

df = pd.read_csv('netflix_titles.csv')

# load the retriever model from huggingface model hub
retriever = SentenceTransformer("multi-qa-MiniLM-L6-cos-v1", use_auth_token=False)

training = pd.DataFrame()
for index, row in df.iterrows():
    prediction_id = random.randint(0, 10000)
    encoding = retriever.encode(row['description']).tolist()
    training = pd.concat(training, pd.DataFrame([{'prediction_id': prediction_id, 'prediction': 1, 'actual': random.randint(0, 1),
                                                  "description": row['description'], 'description_vector': encoding,
                                                  'prompt': f"Give me a movie description about a movie called row['title'].\n\nDescription: "}]))

# Declare which columns are the feature columns
feature_column_names = ['prompt']

# Declare embedding feature columns
embedding_feature_column_names = {
    # Dictionary keys will be the name of the embedding feature in the app
    "embedding_display_name": EmbeddingColumnNames(
        vector_column_name="description_vector",  # column name of the vectors, required
        data_column_name="description",  # column name of the raw data vectors are representing, optional
    )
}

# Define the Schema, including embedding information
schema = Schema(
    prediction_id_column_name="prediction_id",
    prediction_label_column_name="prediction",
    actual_label_column_name="actual",
    feature_column_names=feature_column_names,
    embedding_feature_column_names=embedding_feature_column_names,
)

# Log the dataframe with the schema mapping
response = arize_client.log(
    model_id="gpt-neo-netflix",
    model_version="1",
    model_type=ModelTypes.BINARY_CLASSIFICATION,
    environment=Environments.TRAINING,
    dataframe=training,
    schema=schema,
)

You will notice that for Arize, we specified the environment as Training, since we will set it as our model baseline. We then pass our movie descriptions we trained the model on, both as text and as vector embeddings to Arize. Arize has an interactive UMAP visualization to represent high dimensional vectors in 2D or 3D spaces, to help determine outliers in our dataset.

Arize usually takes 5–15 minutes to ingest all the data we have now logged. Once the data is in Arize, click on your model, and go to the Datasets tab. Click “Configure Baseline” in the top right corner and select “Pre-production”. We have now set up our model baseline

Deploy model with monitoring

In order to deploy our fine-tuned model to Cerebrium with monitoring enabled its as easy as uploading a serialised model file of our fine-tuned model and adding our Arize Schema and platform arguments. Cerebrium will automatically log all inputs and outputs to Arize.


from cerebrium import Conduit, model_type
from arize.utils.types import ModelTypes, Environments, Schema, Metrics

conduit = Conduit('gpt-neo-netflix', '', [(model_type.ONNX, 'gpt-neo-netflix.onnx')])

feature_column_names=['prompt']

embedding_feature_column_names = {
    # Dictionary keys will be the name of the embedding feature in the app
    "embedding_display_name": EmbeddingColumnNames(
        vector_column_name="description_vector",  # column name of the vectors, required
        data_column_name="description", # column name of the raw data vectors are representing, optional
    )
}

schema = Schema(
    prediction_id_column_name="prediction_id",
    prediction_label_column_name="prediction",
    actual_label_column_name="actual",
    feature_column_names=feature_column_names,
    embedding_feature_column_names=embedding_feature_column_names,
)


platform_args = {
    "model_type": ModelTypes.BINARY_CLASSIFICATION,
    "schema": schema,
    "space_key": "",
    "api_key": "",
    "features": features,
    "environment": Environments.PRODUCTION,
    "embedding_features": embedding_feature_column_names    
}

conduit.add_logger(
    platform=logging_platform.ARIZE, 
    platform_authentication={"space_key": platform_args['space_key'], "api_key": platform_args['api_key']},
    features=platform_args["features"],
    targets=["prediction"],
    platform_args=platform_args,
    log_ms=True
)

conduit.deploy()


Your fine-tuned GPT Neo model should now be deployed on Serverless GPU’s with all monitoring being sent to Arize. To read more about the capabilities we offer you can read more in our documentation here.


Monitor in production

In order to demonstrate how we can pick up the cause for the degradation in our model performance we are going to be using the embeddings feature from Arize. Below we do two things:

  1. We send prompts in Spanish to Arize — this is completely different to the prompts we used during fine-tuning.
  2. We fake the generations as Spanish to show you what data that is very semantically different looks like in Arize

Below is the code snippet we used to generate this fake data:


fake_data = [
  {"text": "El español es, hoy en día, nombrado en cada vez más contextos, tomando realce internacional como lengua de cultura y civilización siempre de mayor envergadura."},
  {"text": "Por ahora, sólo un esbozo de manual de estudio de lenguas, facilitando el uso de los recursos que nos ofrecen tanto las wikis como otros enlaces externos."},
  {"text": "Muchas personas piensan que no pueden llegar a aprender una lengua porque la han estado estudiando en la escuela durante muchos años y siguen sin ser capaces de conversar o de entender a un nativo."},
  {"text": "Existen multitud de formas de acercarse a otra lengua/cultura, (viendo películas en versión original subtituladas, leyendo revistas, periódicos o páginas web de algún tema de especial interés para el estudiante, libros infantiles o de lectura graduada, chateando, escuchando audiolibros con la ayuda de una edición impresa del mismo, ...) y lo ideal es combinarlas para optimizar los resultados de proceso de aprendizaje."},
  {"text": "En resumen, para la adquisición de un idioma, el estudiante o hablante requiere entrenarse en la adquisición de las cuatro estrategias lingüísticas que estructuran el dominio de una lengua: escucha, habla, lectura y escritura."},
  {"text": "Por supuesto, al inicio hablaremos con mucha deficiencia y hasta articularemos fonemas no adecuados, pero en ese momento, estamos comunicándonos en el idioma a aprender."},
  {"text": "Con el dominio de la lecto-escritura, se estima que ya el hablante está capacitado para la adquisición de las reglas gramaticales de dicha lengua, que le permitirán conversar, leer y escribir con ciertos niveles de corrección en el marco de la lingüística."}
  {"text": "En este apartado habría que incluir enlaces a los artículos de wikipedia y también a enlaces externos donde haya información sobre determinados métodos y escuelas pedagógicas, pero no voy a dar ninguna direccion en especial porque esta pagina es solamente educacional y no esta permitido publicar nada."},
  {"text": "La memorización de vocabulario suele plantear diversos problemas, la capacidad de retención del estudiante, los cognados que el estudiante ya conoce, la relación genética entre la lengua materna del estudiante y la segunda lengua, correspondencia fonografológica de las palabras que se estudian, falsos amigos"},
  {"text": "La necesidad : Es conveniente que el estudiante se centre en aquél léxico que esté más relacionado con las actividades con las que va a utilizar la segunda lengua."},
  {"text": "Una de las equivocaciones más comunes de los estudiantes cuando leen un texto en una segunda lengua es intentar conocer el significado de todas las palabras."},
  {"text": "El estudiante debería intentar reproducir el mismo proceso que aplica al modo en que reacciona cuando aparecen palabras que desconoce al leer un texto en su lengua materna."},
  {"text": "(Es interesante para profesores que puedan llegar a impartir cursos de educación a distancia, conocer las consideraciones previas que hay que saber para que los alumnos aprendan con éxito."},
  {"text": "El pronombre vos conjugado con la 5ª inflexión verbal sin modificar, es usado en forma literaria para indicar gran respeto, por ejemplo, al dirigirse a Dios o a un rey: vos, Señor, sois mi guía."},
  {"text": "En español hay 6 inflexiones verbales básicas, denominadas tradicionalmente primera persona singular, segunda persona singular, tercera persona singular, primera persona plural, segunda persona plural y tercera persona plural."},
  {"text": "Por ejemplo, es más claro decir usted utiliza la tercera inflexión verbal que decir usted (pese a ser segunda persona) utiliza los verbos en tercera persona"},
  {"text": "Análogamente, es preferible decir vos utiliza la quinta inflexión verbal modificada que decir vos (pese a ser singular) utiliza los verbos en plural, de manera modificada"},
  {"text": "Algunos diminutivos irregulares: novio/noviecito, pez/pececito, Carlos/Carlitos, mano/manito/manita (manita se usa en México y España), caliente/calentito."},
  {"text": "Hay muchas palabras que no tienen diminutivo, como edad/*edadita, bondad/*bondadita, ciudad/*ciudadita (y sí maldad/maldadita), útil/*utilito (y sí fácil/facilito), lunes/*lunesito viernes/*viernesito (y sí sábado/sabadito, domingo/dominguito), espacio/*espacito (y sí despacio/despacito), lenguaje/*lenguajito/*lenguajecito, apetito/*apetitito, termita/*termitita."},
  {"text": "En algunos países, particularmente Colombia, Cuba, Venezuela y algunas regiones de España como Navarra y La Rioja, las terminaciones tito/tita se cambian por tico/tica: zapato/zapatico (lo prefieren a zapatito). Otros ejemplos: pato/patico, rato/ratico."},
  {"text": "Este texto está diseñado para el primer o segundo año de ingeniería con especialidad en comunicaciones (telecomunicaciones), computadores, software o en el área de la electricidad/electrónica."},
  {"text": "Se cubre la manipulación básica de señales, propiedades de las señales, convolución, trasformadas de Fourier y otro material apropiado para nivel de principiantes en el Análisis de Señales."},
  {"text": "La convolución puede ser pensada como un método de fuerza bruta para lograr esto, mientras que los otros métodos convierten la señal desde el dominio del tiempo, al dominio de la frecuencia, donde calcular la salida es mucho más fácil."},
  {"text": "Una vez que todo este material básico ya esté cubierto, entonces se cubrirá el material restante(por cierto más interesante), por ejemplo filtros, el dominio de la frecuencia, la relación entre ancho de banda y el dominio del tiempo, la compresión de audio, porqué los cd muestrean a 44.1 KHz, etc."},
  {"text": "Este texto es un primer aporte y pido, por anticipado disculpas por todos los errores que en él se encuentren, errores que espero corregir con el tiempo."},
  {"text": "Por ahora el texto solamente corresponde a una traducción del original en inglés, en una segunda etapa de desarrollo me permitiré cambiar el orden y los tópicos que en él se utilicen para mejorarlo lo maximo que pueda."},
  {"text": "Fisicamente existen efectos en la naturaleza a los que se puede asociar esta función como por ejemplo la fuerza aplicada en un lapso muy corto, como cuando un martillo golpea un clavo, o la presencia de un voltaje por un instante muy corto que en terminos de esta función como:"},
  {"text": "En el caso de la función escalón, fisicamente representa un cambio instantáneo que se produce a t=0, es una suposición el hecho de representar una función con tiempos negativos (lo cual no existe), en cambio sirve para representar el caso de un interruptor que permanece abierto hasta que en un instante se cierra, estableciendo el máximo voltaje a una carga."},
  {"text": "Si es mayor que 0 (cero) , entonces el valor será igual a la integral de 1 desde el tiempo 0 hasta el tiempo t, la cual también tiene el valor t, es decir:"},
  {"text": "Visto desde el punto de vista matemático una es la derivada de la otra puesto que; la función rampa se deriva de la función escalón, y ésta a su vez de la impulso"},
  {"text": "Este capítulo profundiza en las señales: ¿qué son?, ¿cuáles son? y ¿cuáles son sus propiedades? Estas propiedades se usan para describir características de las señales."},
  {"text": "También se cubren temas de transformaciones de señales, estas transformaciones son sólo matemáticas (conceptualmente se transforman la señal, no se diseñará un sistema para hacerlo)."},
  {"text": "Una señal es cualquier fenómeno que puede ser representado de manera cuantitativa mediante una función continua (cuyo dominio es los números reales) o discreta (cuyo dominio es los números enteros)."},
  {"text": "Su importancia en la tecnología es que, los computadores y microchips que son utilizados en este nuevo mundo Digital en el que vivimos, sólo manejan señales discretas."},
  {"text": "Esto se logra al multiplicar afuera del argumento por una constante mayor a 0 esto dará como resultado un amplificación en el caso contrario si multiplicas por una constante menor a 0 tendrás una atenuación de la señal."},
  {"text": "Se dice que un sistema lineal es invariante en el tiempo si un desplazamiento en el tiempo de la entrada resulta en un desplazamiento idéntico de la salida sin que cambie la forma de onda o perfil de la señal."},
  {"text": "Por consiguiente, en un Sistema Lineal Invariante en el Tiempo (LIT), la respuesta impulsional dependerá únicamente de la diferencia formula_33, es decir,"},
  {"text": "vale 0 en los valores negativos ya tenemos uno de los límites de integración y si aparte consideramos que a formula_43 lo despejamos y lo corremos veces el otro límite de integracion queda definido por el valor"},
  {"text": "11 las derivadas de las señales sinousidales dependen del número de frecuencia de forma analogica segun la señal si es discreta y continua, que básicamente una señal discreta también cuenta con una señal continua."},
  {"text": "Por ejemplo, en vez de usar ecuaciones diferenciales, mediante la transformada de Laplace, convertimos dichas ecuaciones en polinomios, que son de menor dificultad resolutiva."},
  {"text": "El enfoque que se sigue en este curso intenta brindar una aproximación práctica con un énfasis en las implementaciones computacionales de algunos programas para grafos."},
]

import random
prod = pd.DataFrame()
for row in fake_data:
  encoding = retriever.encode(row['text']).tolist()
  prod = pd.concat(([prod, pd.DataFrame([{'prediction_id': random.randint(11000,20000), 'prediction': 1, 'actual': 0, "description": row['text'], 'description_vector': encoding, 'prompt': f"Michael es un espía de fama mundial, pero también un multimillonario hecho"}])]))
  
  
response = arize_client.log(
    model_id="gpt-neo-netflix",
    model_version= "1",
    model_type=ModelTypes.BINARY_CLASSIFICATION,
    environment=Environments.PRODUCTION,
    dataframe=prod,
    schema=schema,
)  

For the above script, we use the same Arize client and schema we used above to send our training data to Arize. After about 10–15 minutes you should see your data in Arize. In your Arize space, click the gpt-neo-neflix model, click the embeddings tab and then click on the Euclidean Distance number.


Above we see our data in Arize in a reduced dimensional space (UMAP). There are a few things to point out here:

  1. We can see the Euclidean distance (drift) of our production embeddings compared to our baseline (training) embeddings. The higher the euclidean distance between two vectors the more different they are. Here you can detect drift over time if the euclidean distance increases over time.
  2. We can see that this cluster (red) is dramatically further from our training data in the blue and green showing how dissimilar our production data is to the data we trained on. By clicking on points in the red cluster we can see the features as well as the prediction given to determine what is going wrong.
  3. Arize automatically collects clusters for us based on parameters that we can change (you can do so in the parameters tab in the UMAP Comparison window). For cluster 1 it shows a purity score of -0.97 meaning the model is seeing production data that is very different from the training data.

Taking the 3 points above into consideration Arize allows us to deep dive into why our fine-tuned model is performing differently in production as opposed to training. Using the ability to filter by cluster, features, datasets, incorrect predictions and more we can come to a conclusion relatively quickly that users are entering Spanish prompts, generating Spanish data. We can easily correct this by doing a check that users are entering prompts in English.


We have only touched the surface in this article about how you can deploy and monitor your models in production using Cerebrium and Arize. If you would like to stay up to date with our latest releases and community please join our Slack, Twitter and/or Discord.

arrow_back
Back to blog