Customer Churn Prediction Using ANN with TensorFlow in Python

In this tutorial, we will discuss customer churn prediction using ANN with TensorFlow deep learning Python module. We will use Keras API built on top of TensorFlow

ANN:-Artificial Neural Networks.

Churn tells you how many existing customers are leaving your business, so lowering churn has a big positive impact on your revenue streams. Churn is a good indicator of growth potential.

You can download the data set from here Customer churn dataset.

So let’s try to implement the code

Import the necessary Python libraries.

import pandas as pd
from matplotlib import pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow import keras
%matplotlib inline

The first step is Data Collection,

1. Load data

c_churn=pd.read_csv("C:\\Users\\usersdrive\\OneDrive\\Desktop\\Customer churn dataset.csv")

To check whether the data set is loaded or not print the first five rows by using the head method.

c_churn.head()

OUTPUT:

Now print the last five rows by using the tail method.

c_churn.tail()

OUTPUT:

Remember this point whenever u are working on machine learning problems that need data exploration.

For churn prediction, the customer id column is not useful so remove the 1 st column.

c_churn.drop('customerID',axis='columns',inplace=True)

To check whether the column is dropped or not.

c_churn.dtypes

OUTPUT:

By seeing the above output the customer id column is dropped successfully.

The customer will be moved out because of the high total charges values or monthly charges.

let’s convert it into float variables.

The monthly charges are in float, the total charges are in an object.

c_churn.TotalCharges.values

OUTPUT:

array(['29.85', '1889.5', '108.15', ..., '346.45', '306.6', '6844.5'],
      dtype=object)

Values are in string format.

Checking the monthly charges

c_churn.MonthlyCharges.values

OUTPUT:

array([ 29.85,  56.95,  53.85, ...,  29.6 ,  74.4 , 105.65])

To change into the float first need to check a null value is present in the dataset.

To check null values.

pd.to_numeric(c_churn.TotalCharges,errors='coerce').isnull()
#means if errors is present then it will fill up with some values

OUTPUT:

To check any null variables present in the dataset.

c_churn[pd.to_numeric(c_churn.TotalCharges,errors='coerce').isnull()]

OUTPUT:

In the dataset, there is a total of 11 rows that are null in the total charges column.

If the null values are present in the dataset then the accuracy will not predict correctly.

It’s better to remove the null values.

To check the dataset size.

c_churn.shape

OUTPUT:

(7043, 20)

Need to check which columns are present in the dataset.

c_churn.iloc[488]

OUTPUT:

Remove the 11 rows in which the null values are present.

df1=c_churn[c_churn.TotalCharges!=' ']
df1.shape

OUTPUT:

(7032, 20)

To check which variables are present in df1.

OUTPUT:

The Total charges are in the object to change into the float using the numeric function.

pd.to_numeric(df1.TotalCharges)

OUTPUT:

To check the total charges in which datatype.

df1.TotalCharges.dtypes

OUTPUT:

dtype('Float')

The second step is

DATA VISUALIZATION

Visualize in the form of a histogram graph.

#lets do the graph for the customers no of tenure
tenure_churn_no=df1[df1.Churn=='No'].tenure
tenure_churn_yes=df1[df1.Churn=='Yes'].tenure
plt.xlabel('tenure')
plt.ylabel("Number of customers")
plt.title("Customer Churn Prediction Visualization")
plt.hist([tenure_churn_yes,tenure_churn_no],label=['churn=Yes','churn=No'])
plt.legend()

OUTPUT:

The above histogram is for tenure.

Now can check for total charges.

mc_churn_no=df1[df1.Churn=='No'].MonthlyCharges
mc_churn_yes=df1[df1.Churn=='Yes'].MonthlyCharges

plt.xlabel('Monthly Charges')
plt.ylabel("Number of customers")
plt.title("Customer Churn Prediction Visualization")

plt.hist([mc_churn_yes,mc_churn_no],label=['churn=Yes','churn=No'],color=['Green','red'])
plt.legend()

OUTPUT:

By seeing the above output we can analyze the company is in trouble.

The machines will not understand the binary values so change the variables into binary.

LABEL ENCODING

def print_unique_col_values(c_churn):
    for column in c_churn:
        if c_churn[column].dtypes=='object':
            print(f'{column} : {c_churn[column].unique()}')

Change into the binary values.

df1.replace('No internet service','No',inplace=True)
df1.replace('No phone service','No',inplace=True)
print_unique_col_values(df1)

OUTPUT:

Now change the remaining columns with the 0 or 1 binary values.

yes_no_columns=['Partner','Dependents','PhoneService','MultipleLines','OnlineSecurity','OnlineBackup','DeviceProtection',
               'TechSupport','StreamingTV','StreamingMovies','PaperlessBilling','Churn']

for col in yes_no_columns:
    df1[col].replace({'Yes': 1,'No': 0},inplace=True)
for col in df1:
    print(f'{col}: {df1[col].unique()}')

OUTPUT:

Replace male and female with 0 and 1.

df1['gender'].replace({'Female':1,'Male':0},inplace=True)
df1['gender'].unique()

OUTPUT:

array([1, 0], dtype=int64)

But still, in the contract function there are three types of variables then use the panda’s dummy function.

#in case of contract function there is 3 objects thats why we use dummy fuctions in pandas
df2=pd.get_dummies(data=df1,columns=['InternetService','Contract','PaymentMethod'])
df2.columns

OUTPUT:

Now all the column values are in binary values lest check with the code.

df2.sample(4)

OUTPUT:

Now all are in either integer or float type.

df2.dtypes

OUTPUT:

SCALING STEP

For three columns that are not present in binary values to get binary values, we need to do the scaling.

cols_to_scale=['tenure','MonthlyCharges','TotalCharges']
from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler()
df2[cols_to_scale]=scaler.fit_transform(df2[cols_to_scale])

To check the data

df2.sample(3)

OUTPUT:

To check the unique values.

for col in df2:
    print(f'{col}: {df2[col].unique()}')

OUTPUT:

Now the full dataset is in binary form

Now implement the test and train data.

X=df2.drop('Churn',axis='columns')
y=df2['Churn']
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=5)
X_train.shape

OUTPUT:

(5625, 26)

Let’s check the test dataset.

X_test.shape

OUTPUT:

(1407, 26)

Let’s check the variables in the train.

X_train[:10]

OUTPUT:

Checking the length of the training dataset.

len(X_train.columns)

OUTPUT:

26

MODEL EVALUATION

import tensorflow as tf
from tensorflow import keras
model=keras.Sequential([
    keras.layers.Dense(26,input_shape=(26,),activation='relu'),
    keras.layers.Dense(1,activation='sigmoid'),
])

model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy'])

model.fit(X_train,y_train,epochs=100)

OUTPUT:

The accuracy is 82 percent which is good.

Evaluation of model.

model.evaluate(X_test,y_test)

OUTPUT:

44/44 [==============================] - 0s 823us/step - loss: 0.4590 - accuracy: 0.7775
[0.4590034484863281, 0.7775408625602722]

To check the array values.

yp=model.predict(X_test)
yp[:5]

OUTPUT:

44/44 [==============================] - 0s 848us/step
array([[0.14974326],
       [0.36690223],
       [0.00675797],
       [0.80207884],
       [0.40584573]], dtype=float32)

The array is in the 2-dimensional format and needs to change to the 1-dimensional format.

y_test[:10]

OUTPUT:

Convert 2d addy into 1 d array.

y_pred=[]
for element in yp:
    if element > 0.5:
        y_pred.append(1)
    else:
        y_pred.append(0)
y_pred[:10]

OUTPUT:

[0, 0, 0, 1, 0, 1, 0, 0, 0, 0]

Need to check the classification reports.

from sklearn.metrics import confusion_matrix,classification_report
print(classification_report(y_test,y_pred))

OUTPUT:

Lets crosscheck with the heatmap.

import seaborn as sn
cm=tf.math.confusion_matrix(labels=y_test,predictions=y_pred)

plt.figure(figsize=(10,10))
sn.heatmap(cm,annot=True,fmt='d')
plt.xlabel('Predicted')
plt.ylabel('Truth')

OUTPUT:

Diagnolas are correct predictions and non diagnoals are not correct predictions.
Accuracy

round((862+229)/(862+229+137+179),2)

OUTPUT:

0.78

precision for 0 th class

round(862/(862+179),2)

OUTPUT:

0.83

precision for 1 class precision for customers who are actually churned.

round(229/(229+137),2)

OUTPUT:

0.63

Recall for 0 th class.

round(862/(862+137),2)

OUTPUT:

0.86
round(229/(229+179),2)

OUTPUT:

0.56

 

Leave a Reply

Your email address will not be published. Required fields are marked *