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

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`