https://d226lax1qjow5r.cloudfront.net/blog/blogposts/building-a-user-churn-prediction-model-with-scikit-learn-and-vonage-dr/Blog_Sickit-Learn_Python_1200x600.png

Creación de un modelo de predicción de bajas de usuarios con Scikit-Learn y Vonage

Tiempo de lectura: 13 minutos

Conversation API de Vonage permite a los desarrolladores construir su propia solución de centro de contacto. Con voz, texto e integraciones a otras soluciones, Vonage te permite construir una solución compleja, sin que el desarrollo sea complejo.

Al crear su solución de centro de contacto, necesita que sea inteligente. Las soluciones más potentes utilizan la IA para enrutar llamadas, traducir textos, recomendar productos, etcétera. Lo mejor es que no hace falta ser un investigador de IA con un doctorado para integrar la IA en su aplicación. Tampoco tienes que depender de un sistema de terceros. Esto parece imposible al principio, pero ha habido un montón de progreso en muchas bibliotecas de aprendizaje automático para que usted, como desarrollador, pueda construir un sistema de aprendizaje automático en su solución. En este post, vamos a ver en la adición de una forma de predecir la probabilidad de que un cliente churning.

¿Qué es la rotación?

El churn, por definición, es el número de clientes que han dejado de utilizar su producto, dividido por el número de clientes totales. Por ejemplo, una empresa con un churn del 1% mensual con 1.000 clientes significa que 10 de cada 1.000 clientes dejan de utilizar el servicio de la empresa cada mes. Se utiliza como indicador de la marcha de la empresa. Una empresa con baja rotación de clientes suele significar que los clientes se quedan. Un churn alto significa que los clientes utilizan el producto o servicio, pero luego lo abandonan.

Un centro de contacto es uno de los lugares donde los clientes interactúan directamente con la empresa, especialmente con el servicio de atención al cliente. Y perder a un cliente con un mal soporte podría tener un impacto en el churn y, por tanto, en su salud como empresa.

En esta publicación, veremos cómo crear una aplicación que simule una conversación entre un cliente y un agente y que pueda predecir la probabilidad de pérdida de clientes, usando la Conversation API de Vonage.

Video showing demo of churn

Visión general

En nuestra demostración, tenemos dos usuarios: un cliente y un agente. Para este ejemplo, supondremos que la empresa es un proveedor de servicios de televisión y que el cliente tiene una pregunta sobre su servicio. También supondremos que el cliente lleva un tiempo con la empresa, y tenemos datos que lo corroboran.

En nuestro ejemplo, tenemos cierta información sobre el usuario. Esto podría ser:

  • El tiempo en meses que el cliente ha utilizado el servicio.

  • Su forma de pago actual. (Cheque, tarjeta de crédito..)

  • Servicios que utilizan. (TV en directo, descodificador, servicios de streaming.. )

  • Y muchos más.

En nuestra demostración, cuando el cliente interactúa con el agente, mostramos la probabilidad de que el usuario cambie de proveedor en la pantalla del agente nada más interactuar. Esto podría ser útil para el agente antes de iniciar la conversación, de modo que podría prestar más atención al cliente, en función de la probabilidad de abandono.

Requisitos previos

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Utilizaremos Hui Jing Chende Hui Jing de Hui Jing Chen como punto de partida. Añadiremos nuestra funcionalidad de predicción de rotación sobre esta aplicación.

Para ejecutar nuestra aplicación localmente, clonaremos el repositorio repoy tendremos que usar ngrok. Si no estás familiarizado con Ngrok, por favor consulta nuestro Tutorial de Ngrok antes de continuar.

Antes de repasar la aplicación, primero tenemos que construir nuestro modelo. Y para construirlo, vamos a utilizar Cuadernos Jupyter que se ejecutan en Google Colab. Un Jupyter Notebook es una forma interactiva de ejecutar código y es ampliamente utilizado en la ciencia de datos y aprendizaje automático. Google Colab es un servicio gratuito que permite ejecutar estos cuadernos en la nube. El código para construir el cuaderno se encuentra aquíPara ejecutar este cuaderno, súbalo a Google Colab.

Para este tutorial, vamos a suponer que usted tiene una comprensión básica de lo que es el aprendizaje automático, pero usted no tendrá que entender todo para seguir adelante.

Para construir un modelo, primero necesitamos datos. Y para este ejemplo, utilizaremos Telecom Churn Dataset de IBM. Este conjunto de datos contiene 7043 filas de datos de usuarios anónimos de una empresa de telecomunicaciones. Para entender el conjunto de datos, echaremos un vistazo a las 10 primeras filas de los datos utilizando Pandas. Pandas es una biblioteca de Python para procesar y comprender datos. Para cada usuario, tenemos 23 columnas, también conocidas como características. Incluyen el sexo del cliente (hombre, mujer), la antigüedad (cuánto tiempo lleva siendo cliente) y si tiene diferentes servicios, como teléfono, Internet y televisión.

Para construir nuestro modelo, primero tenemos que asegurarnos de que no hay valores vacíos en el conjunto de datos. Si no comprobamos esto e intentamos construir nuestro modelo, tendremos errores.

Leamos nuestro conjunto de datos y eliminemos los valores vacíos.

df = pd.read_csv("/content/WA_Fn-UseC_-Telco-Customer-Churn.csv")
df = df.dropna(axis='columns', inplace=True)

A continuación, utilice df.head() para ver las 10 primeras filas de datos. Cuando miramos estos datos, vemos que muchas de las filas contienen cadenas. (YES, NO). Ahora tenemos que convertir estas cadenas en números, porque los modelos de aprendizaje automático sólo saben tratar con números.

Para cada fila que contenga una cadena, necesitamos ver si las cadenas son únicas. Para ver todos los valores posibles en esta columna, pandas tiene una función, llamada unique() que lo hace por nosotros.

df.Partner.unique()

devoluciones: array(['Yes', 'No'], dtype=object)

Esto significa que la fila sólo contiene los valores YES y NO. Para esta columna, podemos convertir esas cadenas en booleanos(1,0). Sin embargo, si miramos las otras filas, por ejemplo PaymentMethod, hay más valores que YES o NO.

df.PaymentMethod.unique()

devuelve array(['Electronic check', 'Mailed check', 'Bank transfer (automatic)', 'Credit card (automatic)'], dtype=object)

Así que para esta columna, tenemos que hacer un poco más de trabajo. Siempre que un valor sea YES o NOpodemos convertirlo en 1 o 0respectivamente. Cuando sea cualquier otra cadena, convirtámosla en -1. Una vez más, el modelo de aprendizaje automático sólo puede utilizar números, así que por eso lo establecemos en -1.

Si nos fijamos en las otras columnas, PhoneService, MultipleLines, OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport, StreamingTVy StreamingMoviesparecen ser similares. Así que vamos a escribir una función que va a través de cada columna y convierte nuestras cadenas en ints.

numeric_features = ['Partner', 'Dependents', 'PhoneService', 'MultipleLines','OnlineSecurity', 'OnlineBackup','DeviceProtection', 'TechSupport','StreamingTV', 'StreamingMovies', 'PaperlessBilling', 'Churn']
def to_numeric(s):
  if s == "Yes":
    return 1
  elif s == "No":
    return 0
  else: return -1

for feature in numeric_features:
  df[feature] = df[feature].apply(to_numeric)

Numeric_features es una lista de todas las columnas que necesitamos actualizar. to_numeric es una función que toma el valor de cada fila y convierte la cadena en un int. Por último, recorreremos todos los elementos de to_numeric y llamaremos a la función pandas apply para llamar a nuestra función. Echemos un vistazo a las 10 primeras filas para verificarlo.

Parece que esas filas ya son válidas, pero tenemos que ocuparnos de las demás columnas. Primero inspeccionemos Contact y veamos los valores mostrados.

df.Contract.unique()

que devuelve: array(['Month-to-month', 'One year', 'Two year'], dtype=object)

Estos valores siguen siendo cadenas, pero no es tan fácil como convertirlos a 1y 0's. Hay otras columnas en este conjunto de datos que son similares, incluyendo PhoneService, MultipleLines, InternetService, OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport, StreamingTV, StreamingMovies, Contract, PaperlessBillingy PaymentMethod.

Por suerte, en pandas, podemos convertir estos valores usando get_dummies(). Esta función, cuando se aplica a una columna, creará una nueva columna para cada valor posible. Y cada valor en cada una de estas nuevas columnas será 1 o 0. Esto también se conoce como codificación en un solo paso.

Por ejemplo, tomemos la columna Contract que contiene los valores de Month-to-month, One yeary Two year. Utilizando get_dummies()crearemos 3 nuevas columnas llamadas Contract_Month-to-month, Contract_One year y Contract_Two year. Y cada valor en estas columnas será 1 o 0.

categorical_features = [
 'PhoneService',
 'MultipleLines',
 'InternetService',
 'OnlineSecurity',
 'OnlineBackup',
 'DeviceProtection',
 'TechSupport',
 'StreamingTV',
 'StreamingMovies',
 'Contract',
 'PaperlessBilling',
 'PaymentMethod']
df = pd.get_dummies(df, columns=categorical_features)

Aquí, creamos una lista de estas características que queremos convertir en categóricas y llamamos a la función get_dummies utilizando DataFrame(df) y la lista de columnas(categorical_features). Veamos de nuevo las 10 primeras filas para comprobar nuestro trabajo. Como el DataFrame tiene ahora 41 características, enlazaremos con la celda aquí en lugar de mostrar una captura de pantalla del DataFrame

Parece que todas las columnas son numéricas. Vamos a construir nuestro modelo. Para construir nuestro modelo, vamos a utilizar otro paquete llamado scikit-learn. Scikit-Learn tiene muchas funciones incorporadas para procesar y entrenar un modelo con nuestros datos.

Primero, necesitamos 2 matrices X y y. X es una matriz que incluye todas nuestras características excepto la característica que estamos utilizando para hacer predicciones(Churn). Y es sólo el valor de Churnque es un 1 o 0.

X = df.drop(labels='Churn',axis=1)
Y = df.Churn
print(X.shape, Y.shape)

Esto devuelve: ((7043, 40), (7043,)) Lo que significa que hay 7043 filas y 40 columnas en X. Para Ytenemos 7043 filas.

A continuación, tenemos que dividir nuestros datos en un conjunto de entrenamiento y otro de prueba. Al entrenar nuestro modelo, sólo utilizamos una parte del conjunto de datos. El resto de los datos se utiliza para las pruebas. Esto sirve para ver lo bien que nuestro modelo ha aprendido los datos. Usando Scikit-Learn, vamos a dividir nuestro conjunto de datos usando la función entrenar-prueba-dividir() .

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,Y,test_size=0.3, random_state=42)

Por último, podemos construir un modelo.

Para este ejemplo, utilizaremos un modelo sencillo. Pero en el mundo real, tenemos que probar distintos modelos con nuestro conjunto de datos para ver cuál es el mejor. Aquí es donde reside gran parte del trabajo en el aprendizaje automático. No hay reglas rígidas ni rápidas para seleccionar un modelo para tus datos. Siempre depende del conjunto de datos.

Para este ejemplo, utilizaremos LogisticRegression para entrenar nuestro modelo. Es un buen modelo para empezar, ya que generalmente funciona bien haciendo predicciones para valores booleanos. Scikit-learn hace que sea muy sencillo implementar este modelo.

from sklearn.linear_model import LogisticRegression
from sklearn import metrics

model = LogisticRegression()
model.fit(X_train, y_train)

Una vez entrenado el modelo en el conjunto de entrenamiento, podemos utilizarlo en el conjunto de pruebas llamando a la función predict con nuestro conjunto de pruebas (X_test).

model.predict(X_test)
# Print the prediction accuracy
print (metrics.accuracy_score(y_test, prediction_test))

Utilizando metrics.accuracy_score podemos imprimir nuestra precisión, 0,8135352579271179 que es ~89%. Esto significa que cuando nuestro modelo recibe datos del conjunto de pruebas para hacer una predicción, su resultado es correcto en un 89% de las ocasiones. Tenga en cuenta que esta precisión es sólo una de las muchas métricas a la hora de evaluar un modelo.

Una vez que tengamos nuestro modelo, tendremos que guardarlo para utilizarlo en nuestra aplicación de contact center. Para guardar el modelo, utilizamos la función joblib volcar.

También tendremos que guardar los nombres de las columnas que hemos utilizado para el entrenamiento, ya que las utilizaremos cuando construyamos nuestro servidor para hacer predicciones.

model_columns = list(df.columns)
model_columns.remove('Churn')
joblib.dump(model_columns, 'model_columns.pkl')

A continuación, construiremos un sencillo servidor Python para servir este modelo.

Modelo de servicio con Flask

A continuación, crearemos un Flask para crear una aplicación de servidor básica para alojar nuestro modelo. Necesitamos una forma de alojar el modelo y hacer predicciones. Si esto fuera una aplicación de producción, necesitaríamos guardar la información del usuario, que contendría la misma información que utilizamos para el entrenamiento en nuestro conjunto de datos. Esto incluiría cuánto tiempo lleva el usuario en la empresa (titularidad), si tiene InternetService, PhoneService, OnlineBackupetc. De esta forma, cuando hagamos nuestra predicción, haremos una consulta a nuestra base de datos para obtener la información de los usuarios, haremos una predicción a partir de nuestro modelo y obtendremos la probabilidad de abandono. Sin embargo, para este ejemplo, generaremos la información para un usuario y enviaremos esa predicción de vuelta a la aplicación.

En server.pyharemos una aplicación Flask sencilla, que sólo tiene un endpoint llamado /predict. Este endpoint generará los datos del usuario, invocará a nuestro modelo y devolverá la predicción como una respuesta JSON.

Antes de realizar la predicción, primero tenemos que cargar el modelo guardado cuando se inicia el servidor.

app = Flask(__name__)

@app.route('/predict', methods=['GET'])
@cross_origin()
def predict():
  #will return prediction
  return

if __name__ == '__main__':
     model = joblib.load('model/model.pkl')
     model_columns = joblib.load('model/model_columns.pkl')
     app.run()

Aquí usamos joblib para cargar nuestro modelo y las columnas que utilizamos para el entrenamiento. Tenemos que asegurarnos de que hemos copiado model.pkl y model_columns.pkl en nuestra aplicación de servidor. En la función predict() generaremos los datos de un usuario aleatorio, crearemos un nuevo DataFrame a partir de los datos utilizando los nombres de las columnas guardadas.

def predict():
    random_user_data = generate_data()
    #https://towardsdatascience.com/a-flask-api-for-serving-scikit-learn-models-c8bcdaa41daa
    query = pd.get_dummies(pd.DataFrame(random_user_data, index=[0]))
    query = query.reindex(columns=model_columns, fill_value=0)

    #return prediction as probability in percent
    prediction = round(model.predict_proba(query)[:,1][0], 2)* 100
    return jsonify({'churn': prediction})

La función generate_data() crea un nuevo diccionario que contiene las mismas columnas que nuestro conjunto de datos de entrenamiento y asigna un valor aleatorio a cada una.

def random_bool():
    return random_number()

def random_number(low=0, high=1):
    return random.randint(low,high)

def generate_data():
    internetServices = ['DSL', 'Fiber optic', 'No']
    contracts = ['Month-to-month', 'One year', 'Two year']
    paymentMethods = ['Electronic check', 'Mailed check', 'Bank transfer (automatic)','Credit card (automatic)']

    random_data = {
            'name':'customer',
            'Partner': random_bool(),
            'Dependents': random_bool(),
            'tenure': random_number(0,50),
            'PhoneService': random_bool(),
            'MultipleLines': random_number(-1),
            'InternetService': random.choice(internetServices),
            'OnlineSecurity': random_number(-1),
            'OnlineBackup': random_number(-1),
            'DeviceProtection': random_number(-1),
            'TechSupport': random_number(-1),
            'StreamingTV': random_number(-1),
            'StreamingMovies': random_number(-1),
            'Contract': random.choice(contracts),
            'PaperlessBilling': random_bool(),
            'PaymentMethod': random.choice(paymentMethods)
        }
    return random_data

Para InternetService, Contract y PaymentMethodcodificamos los posibles valores que se pueden utilizar para cada uno y elegimos un valor aleatorio. Para las demás características, si sólo contuviera una Yes o No en el conjunto de entrenamiento, asignaremos a 1 o 0Al azar. Yes, No y alguna otra cadena, usaremos 1, 0 y -1respectivamente.

A continuación, vamos a repasar nuestra función de predicción, que se llama cuando hay una solicitud a la/predict punto final.

@app.route('/predict', methods=['GET'])
@cross_origin()
def predict():

    random_user_data = generate_data()
    query = pd.get_dummies(pd.DataFrame(random_user_data, index=[0]))
    query = query.reindex(columns=model_columns, fill_value=0)

    #return prediction as probability in percent
    prediction = round(model.predict_proba(query)[:,1][0], 2)* 100
    return jsonify({'churn': prediction})

Aquí, generamos datos para un usuario aleatorio, luego creamos un DataFrame que se parece al DataFrame que usamos para entrenar nuestro modelo. Este tendrá las mismas columnas pero solo tendrá 1 fila del conjunto de datos.

Por último, llamaremos a predict_proba en el modelo para que devuelva un vector con la probabilidad de que el usuario abandone. [[0.79329917 0.20670083]] Esta matriz contiene la probabilidad de que el usuario abandone (0,79329917) y de que no lo haga (0,20670083). Tomaremos el último elemento del vector, lo redondearemos a dos decimales y lo convertiremos en porcentaje.

prediction = round(model.predict_proba(query)[:,1][0], 2)* 100 Esto devolverá 21.0que es el porcentaje de usuarios que abandonan.

Por último, devolveremos este valor como JSON en un objeto churn objeto.

A continuación, ejecutaremos y desplegaremos nuestro modelo localmente.

En el terminal, vaya a la carpeta model_server y ejecútelo:

pip install flask pandas python server.py

Ahora, nuestro servidor modelo se está ejecutando, y podemos probarlo haciendo una petición GET al endpoint /predict punto final.

An image showing the Model Server in Postman

El servidor devuelve una respuesta JSON que muestra el porcentaje de probabilidad de que un usuario aleatorio abandone.

Aplicaciones Web

Después de construir nuestro modelo de aprendizaje automático y crear un backend para servir nuestras predicciones, ahora lo integraremos en una aplicación web para mostrar las predicciones de rotación a un agente de atención al cliente.

Para mostrar nuestra predicción de churn, crearemos un evento de conversación personalizado llamado churn-prediction que se llamará cuando el servidor modelo devuelva su predicción de abandono para un usuario determinado.

En la carpeta ui_app navegue hasta el archivo common.js dentro de la carpeta public y añadiremos lo siguiente:

function getChurnForUser(conversation) {
  //Send custom event to agent
  if (window.location.pathname == "/") {
    fetch("http://127.0.0.1:3001/predict",{
    mode: 'cors',
    headers: {
      'Access-Control-Allow-Origin':'*'
    }
  })
    .then(response => {return response.json()})
    .then(json => {
      conversation.sendCustomEvent({ type: 'churn-prediction', body: json}).then(() => {
        console.log('custom event was sent');
      }).catch((error)=>{
        console.log('error sending the custom event', error);
      });
    })
    .catch(error => console.log('error', error));
  }
}

Esta función acepta la conversación de activación y llama al servidor de modelos que construimos anteriormente. Hemos codificado la URL y el puerto para facilitar su uso.

En un entorno de producción, pasaríamos el valor user-id para generar una predicción para ese usuario. Pero para este ejemplo, como se ha mostrado antes, generamos la información de un usuario aleatorio para utilizarla en el modelo de predicción de bajas.

A continuación, añadiremos una etiqueta h2 en la pantalla del agente para mostrar la predicción de bajas. A continuación, añadiremos un listener al evento churn-prediction que actualizará el texto de la etiqueta h2 etiqueta. Dentro de la función setupListeners añadiremos esto:

activeConversation.on('churn-prediction', (sender, message) => {
  if (window.location.pathname == "/agent") {
    document.getElementById("churn_text").innerHTML = "Likelihood of customer churn: " +  message["body"]["churn"] + "%"
    console.log(sender, message);
  }
});

Cuando churn-prediction evento, enviamos la predicción del churn en la propiedad message y actualizamos el innerHTML texto con el churn.

Ahora, cada vez que un nuevo usuario hable con nuestro agente, podremos ver la probabilidad de que ese usuario abandone. Si la posibilidad de cancelación es alta, tal vez, deberíamos ser un poco más amables con ellos :)

Conclusión

En general, hemos demostrado que construir un modelo de aprendizaje automático no es magia, y que cualquiera puede construir algo que sea útil y pueda utilizarse en un entorno de producción. Hemos demostrado que nuestro modelo es capaz de predecir la probabilidad de pérdida de clientes con una precisión del 89%. Lo cual está muy bien para nuestro primer modelo. Pero podemos hacer mucho más para construir un modelo aún mejor. Una de las mejores formas de aprender es haciendo, ¡así que veamos si puedes construir un modelo que supere a éste!

Puede utilizar el Google Colab Notebook como proyecto inicial. Como siempre, estaremos encantados de ayudarte con cualquier pregunta en nuestra comunidad slack.

Compartir:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Tony HungAntiguos alumnos de Vonage

Desarrollador IOS convertido en entusiasta de la Ciencia de Datos / Aprendizaje Automático. Quiero que la gente entienda qué es el aprendizaje automático y cómo podemos utilizarlo en nuestras aplicaciones.