In [2]:
# reading and handling the data
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
data = pd.read_csv("pulsar_stars.csv")
# print a part of the dataset
data.head()
Out[2]:
In [3]:
print ("Number of rows :",data.shape[0])
print ("Number of columns :",data.shape[1])
In [4]:
print ("data info :",data.info())
In [5]:
print (data.isnull().sum())
In [6]:
# import packages for plotting
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12,6))
plt.subplot(121)
ax = sns.countplot(y = data["target_class"],
palette=["r","g"],
linewidth=1,
edgecolor="k"*2)
for i,j in enumerate(data["target_class"].value_counts().values):
ax.text(.7,i,j,weight = "bold",fontsize = 27)
plt.title("Count for target variable in dataset")
plt.subplot(122)
plt.pie(data["target_class"].value_counts().values,
labels=["not pulsar stars","pulsar stars"],
autopct="%1.0f%%",wedgeprops={"linewidth":2,"edgecolor":"white"})
my_circ = plt.Circle((0,0),.7,color = "white")
plt.gca().add_artist(my_circ)
plt.subplots_adjust(wspace = .2)
plt.title("Proportion of target variable in dataset")
plt.show()
In [7]:
# import package
import itertools
# rename column headers for convenience
data = data.rename(columns={" Mean of the integrated profile": "Mean profile", " Standard deviation of the integrated profile": "StD profile",
" Excess kurtosis of the integrated profile": "Kurtosis profile", " Skewness of the integrated profile": "Skewness profile",
" Mean of the DM-SNR curve": "Mean DM-SNR", " Standard deviation of the DM-SNR curve": "StD DM-SNR",
" Excess kurtosis of the DM-SNR curve": "Kurtosis DM-SNR", " Skewness of the DM-SNR curve": "Skewness DM-SNR",
"target_class": "Target class"})
# distribution of variable in the dataset
columns = ['Mean profile', 'StD profile', 'Kurtosis profile', 'Skewness profile',
'Mean DM-SNR', 'StD DM-SNR', 'Kurtosis DM-SNR',
'Skewness DM-SNR']
length = len(columns)
colors = ["r","g","b","m","y","c","k","orange"]
plt.figure(figsize=(13,20))
for i,j,k in itertools.zip_longest(columns,range(length),colors):
plt.subplot(length/2,length/4,j+1)
sns.distplot(data[i],color=k)
plt.title(i)
plt.subplots_adjust(hspace = .3)
plt.axvline(data[i].mean(),color = "k",linestyle="dashed",label="MEAN")
plt.axvline(data[i].std(),color = "b",linestyle="dotted",label="STANDARD DEVIATION")
plt.legend(loc="upper right")
In [8]:
# correlation matrix
corr_matrix = data.corr()
# the figure needs to be corrected manually so we just load here the saved file
# just uncomment the lines below to run the plot (ctrl + /)
# plt.figure(figsize=(15,7))
# sns.heatmap(corr_matrix,annot=True,linewidth=2,edgecolor='k')
# plt.title('Correlation matrix')
# plt.savefig('corrmatrix.png')
# plt.show()
In [9]:
# Pair plots
sns.pairplot(data,hue = "Target class")
plt.title("Pair plot for variables")
# plt.savefig('pairplot.png',bbox_inches='tight')
plt.show()
In [10]:
# Violin Plot
columns = [x for x in data.columns if x not in ["Target class"]]
length = len(columns)
plt.figure(figsize=(13,25))
for i,j in itertools.zip_longest(columns,range(length)):
plt.subplot(length/2,length/4,j+1)
sns.violinplot(x=data["Target class"],y=data[i],
palette=["Orangered","lime"],alpha=.5)
plt.title(i)
#plt.savefig('violinplot.png',bbox_inches='tight')
plt.show()
Here, we obtain the log-likelihood and our cost/loss function
$$ \mathcal{C}(\hat{\beta}) = \sum_{i=1}^n \left( y_i\log{p(y_i=1|x_i,\hat{\beta})} + (1-y_i)\log\left[1-p(y_i=1|x_i,\hat{\beta}))\right]\right). $$The MLE is defined as the set of parameters that maximize the log-likelihood with respect to $\beta$. The right hand side of the above equation is known as the cross entropy in statistics. The cross entropy is a convex function of the weights $\hat{\beta}$. Hence, any local minimizer is a global minimizer. Here, one must perform numerical methods to solve the optimization problem.
In this project, we used the available functionality of scikit-learn for logistic regression, which is LogisticRegression. The cross-validation resampling technique (described later) was also added to compute the accuracy scores for the test and training data. The code is describled as follows:
In [11]:
##############################################
# Logistic regression with cross-validation #
##############################################
# import packages
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.metrics import confusion_matrix
# We define the hyperparameters to be used
nlambdas = 500 # why not? the code runs relatively fast
lmbdas = np.logspace(-5, 5, nlambdas)
kfold = KFold(n_splits = 5) #cross validation spliting
# We preallocate data
# true values that will be found later
train_accuracy=np.zeros(lmbdas.shape,np.float64)
test_accuracy=np.zeros(lmbdas.shape,np.float64)
train_red_accuracy=np.zeros(lmbdas.shape,np.float64)
test_red_accuracy=np.zeros(lmbdas.shape,np.float64)
# dummy arrays to be averaged later on
train_accuracy_d=np.zeros(5,np.float64)
test_accuracy_d=np.zeros(5,np.float64)
train_red_accuracy_d=np.zeros(5,np.float64)
test_red_accuracy_d=np.zeros(5,np.float64)
# We create the design matrix X and separate the labels into Y
x_fea = [x for x in data.columns if x not in ['Target class']]
#print(x_fea)
X = np.zeros((data.shape[0],data.shape[1]-1))
X_red = np.zeros((data.shape[0],3))
Y = np.zeros(data.shape[0])
for i,feature in enumerate(x_fea): # Here we just take the variables of interest
X[:,i] = data[feature]
if 'Mean profile' == feature:
X_red[:,0]
if 'Kurtosis profile' == feature:
X_red[:,1]
if 'Skewness profile'== feature:
X_red[:,2]
Y[:] = data['Target class']
# We perform a logistic regression for each value of lambda
for i,lmbda in enumerate(lmbdas):
#define model
logreg = LogisticRegression(C=1.0/lmbda,solver='liblinear')
# Perform the cross-validation
j = 0
for train_inds, test_inds in kfold.split(X):
# Do the split
X_train = X[train_inds]
X_red_train = X_red[train_inds]
Y_train = Y[train_inds]
X_test = X[test_inds]
X_red_test = X_red[test_inds]
Y_test = Y[test_inds]
# We will scale the data
scaler = StandardScaler()
scaler.fit(X_train)
# first on full data
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
# then rescale and do on reduced data
scaler.fit(X_red_train)
X_red_train = scaler.transform(X_red_train)
X_red_test = scaler.transform(X_red_test)
del scaler
# calculate accuracies for the k fold
logreg.fit(X_train, Y_train)
train_accuracy_d[j]=logreg.score(X_train,Y_train)
test_accuracy_d[j]=logreg.score(X_test,Y_test)
logreg.fit(X_red_train, Y_train)
train_red_accuracy_d[j]=logreg.score(X_red_train,Y_train)
test_red_accuracy_d[j]=logreg.score(X_red_test,Y_test)
j += 1
del X_red_train,X_red_test,X_train,Y_train,X_test,Y_test # delete useless data
#Average to get accuracy values
train_accuracy[i]=np.mean(train_accuracy_d)
test_accuracy[i]=np.mean(test_accuracy_d)
train_red_accuracy[i]=np.mean(train_red_accuracy_d)
test_red_accuracy[i]=np.mean(test_red_accuracy_d)
#print((i+1)/5,'% done')
#plot
# just uncomment the lines below to run the plot (ctrl + /)
# plt.figure(figsize=(15,7))
# plt.semilogx(lmbdas,train_accuracy,label='train')
# plt.semilogx(lmbdas,test_accuracy,label='test')
# plt.semilogx(lmbdas,train_red_accuracy,label='train reduced')
# # #train and test differ very little so to see the different lines we use '--'
# plt.semilogx(lmbdas,test_red_accuracy,'--',label='test reduced')
# plt.xlabel('$\\lambda$')
# plt.ylabel('$\\mathrm{accuracy}$')
# plt.grid()
# plt.legend()
# # #plt.savefig('logreg.png',bbox_inches='tight')
# plt.show()
where $t_{i}$ are targets, and $y_{i}$ are the outputs of the network. The minimization of the cost function can be done using the following steps:
Initialize the NN with random value of weigths $w^{l}_{ij}$ and biases $b^{l}_{i}$.
Propagate forwards to find outputs $y_{i}$ and compute the corresponding $C(w, b)$.
Use back propagation algorithm to find optimal $w^{l}_{ij}$ and $b^{l}_{i}$.
Repeat steps 2 and 3 until $\frac{\partial C}{\partial w} = 0$ and $\frac{\partial C}{\partial b} = 0$.
In [12]:
################################################
# Neural Network #
################################################
# import packages
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import itertools
from PIL import Image
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix,accuracy_score
from sklearn.neural_network import MLPClassifier
warnings.filterwarnings("ignore")
#Split data with 20% of test data:
np.random.seed(2018)
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size = 0.2, random_state = 66)
# Define the learning rate, hyperparameter using NUMPY
eta_vals = np.logspace(-5, 1, 7)
lmbd_vals = np.logspace(-5, 1, 7)
n_hidden_neurons = 50
epochs = 100
# Use scikit learn for neural network
DNN_scikit = np.zeros((len(eta_vals), len(lmbd_vals)), dtype=object)
for i, eta in enumerate(eta_vals):
for j, lmbd in enumerate(lmbd_vals):
dnn = MLPClassifier(hidden_layer_sizes=(n_hidden_neurons), activation='logistic',
alpha=lmbd, learning_rate_init=eta, max_iter=epochs, solver='adam')
dnn.fit(X_train, y_train)
DNN_scikit[i][j] = dnn
# just uncomment below to print the accuracy scores
#print("Learning rate = ", eta)
#print("Lambda = ", lmbd)
#print("Accuracy score on test set: ", dnn.score(X_test, y_test))
#print()
#Plot the accuracy as function of learning rate and hyperparameter
# just uncomment the lines below to generate the plots (ctrl + /)
# sns.set()
# train_accuracy = np.zeros((len(eta_vals), len(lmbd_vals)))
# test_accuracy = np.zeros((len(eta_vals), len(lmbd_vals)))
# for i in range(len(eta_vals)):
# for j in range(len(lmbd_vals)):
# dnn = DNN_scikit[i][j]
# train_pred = dnn.predict(X_train)
# test_pred = dnn.predict(X_test)
# train_accuracy[i][j] = accuracy_score(y_train, train_pred)
# test_accuracy[i][j] = accuracy_score(y_test, test_pred)
# fig, ax = plt.subplots(figsize = (10, 10))
# sns.heatmap(train_accuracy, annot=True,annot_kws={"size": 18}, ax=ax, cmap="viridis")
# ax.set_title("Training Accuracy",fontsize=18)
# ax.set_ylabel("$\eta$",fontsize=18)
# ax.set_yticklabels(eta_vals)
# ax.set_xlabel("$\lambda$",fontsize=18)
# ax.set_xticklabels(lmbd_vals)
# plt.tick_params(labelsize=18)
# fig, ax = plt.subplots(figsize = (10, 10))
# sns.heatmap(test_accuracy, annot=True,annot_kws={"size": 18}, ax=ax, cmap="viridis")
# ax.set_title("Test Accuracy",fontsize=18)
# ax.set_ylabel("$\eta$",fontsize=18)
# ax.set_yticklabels(eta_vals)
# ax.set_xlabel("$\lambda$",fontsize=18)
# ax.set_xticklabels(lmbd_vals)
# plt.tick_params(labelsize=18)
# #plt.show()
#Plot confusion matrix at optimal values of learning rate and hyperameter
# just uncomment the lines below to generate the plots (ctrl + /)
# dnn = MLPClassifier(hidden_layer_sizes=(n_hidden_neurons), activation='logistic',
# alpha=0.001, learning_rate_init=0.001, max_iter=epochs, solver='adam')
# dnn.fit(X_train,y_train)
# y_pred=dnn.predict(X_test)
# fig1, ax = plt.subplots(figsize = (13,10))
# sns.heatmap(confusion_matrix(y_test,y_pred),annot=True,fmt = "d",linecolor="k",linewidths=3)
# ax.set_xlabel('True label',fontsize=18)
# ax.set_ylabel('Predicted label',fontsize=18)
# ax.set_title("CONFUSION MATRIX",fontsize=20)
# plt.tick_params(labelsize=18)
# plt.show()
# # Feature importance -->weights
# coef=dnn.coefs_[0]
# print (coef)
The code for solving pulsar classification problem using Decision Tree is decribed as follows:
In [8]:
###########################################################
# Decision Tree WITHOUT CROSS VALIDATION #
###########################################################
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import itertools
from PIL import Image
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix,accuracy_score
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_validate # We can use this available functionality. In the code, we built our own CV
from sklearn.ensemble import RandomForestClassifier
warnings.filterwarnings("ignore")
#Split data
np.random.seed(2030)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2, random_state = 66)
# The maximum depth of the tree:
max_degree=20
maxdepth=np.zeros(max_degree) # Make an array with different max_depth
#Initialize accuracy arrays for training and test data for gini and entropy criteria:
train_accuracy_gini=np.zeros(max_degree)
test_accuracy_gini=np.zeros(max_degree)
train_accuracy_entropy=np.zeros(max_degree)
test_accuracy_entropy=np.zeros(max_degree)
for i in range(1,max_degree+1):
maxdepth[i-1]=i
#Decision tree method with gini criteria
tree_gini = DecisionTreeClassifier(max_depth=i)
tree_gini.fit(X_train, Y_train)
train_accuracy_gini[i-1]=tree_gini.score(X_train, Y_train)
test_accuracy_gini[i-1]=tree_gini.score(X_test, Y_test)
#Decision tree method with entropy criteria
tree_entropy = DecisionTreeClassifier(criterion='entropy',max_depth=i)
tree_entropy.fit(X_train, Y_train)
train_accuracy_entropy[i-1]=tree_entropy.score(X_train, Y_train)
test_accuracy_entropy[i-1]=tree_entropy.score(X_test, Y_test)
# just uncomment the lines below to generate the plots (ctrl + /)
# print("accuracy on training data with criterion gini [tree]=",np.around(train_accuracy_gini,decimals=2))
# print("accuracy on test data with criterion gini[tree]=", np.around(test_accuracy_gini,decimals=2))
# print("accuracy on training data with criterion entropy [tree]=",np.around(train_accuracy_entropy,decimals=2))
# print("accuracy on test data with criterion entrpy [tree]=", np.around(test_accuracy_entropy,decimals=2))
# #Plot the accuracy for training and test data as a function of max_depth
# fig, tree=plt.subplots()
# tree.set_xlabel('max_depth')
# tree.set_ylabel('accuracy')
# tree.set_title('Decision Tree')
# tree.plot(maxdepth,train_accuracy_gini, color='r', label='Training [Gini]' )
# tree.plot(maxdepth,test_accuracy_gini, color='r',linestyle="--", label='Test [Gini]' )
# tree.plot(maxdepth,train_accuracy_entropy, color='b', label='Training [entropy]' )
# tree.plot(maxdepth,test_accuracy_entropy, color='b',linestyle="--", label='Test [entropy]' )
# tree.legend()
# CONFUSION MATRIX for gini criteria and max_depth=7
# just uncomment the lines below to generate the plots (ctrl + /)
# tree= DecisionTreeClassifier(max_depth=7)
# tree.fit(X_train,y_train)
# y_pred=tree.predict(X_test)
# fig, matrix = plt.subplots(figsize = (13,10))
# sns.heatmap(confusion_matrix(y_test,y_pred),annot=True,fmt = "d",linecolor="k",linewidths=3)
# matrix.set_xlabel('True label',fontsize=18)
# matrix.set_ylabel('Predicted label',fontsize=18)
# matrix.set_title("CONFUSION MATRIX",fontsize=20)
# plt.tick_params(labelsize=18)
# plt.show()
Including CV resampling technique, the code is written as follows:
In [9]:
#######################################
#Decision Tree WITH CROSS VALIDATION #
#######################################
# Initialize the standard deviation for the accuracy scores
train_var_gini=np.zeros(max_degree,np.float64)
test_var_gini=np.zeros(max_degree,np.float64)
train_var_entropy=np.zeros(max_degree,np.float64)
test_var_entropy=np.zeros(max_degree,np.float64)
k = 10
# dummy arrays to be averaged later on
train_accuracy_gini_d=np.zeros(k,np.float64)
test_accuracy_gini_d=np.zeros(k,np.float64)
train_accuracy_entropy_d=np.zeros(k,np.float64)
test_accuracy_entropy_d=np.zeros(k,np.float64)
# Number of kfold for CV
kfold = KFold(n_splits = k)
#Decision Tree
for i in range(1,max_degree+1):
maxdepth[i-1]=i
tree_gini = DecisionTreeClassifier(max_depth=i)
tree_entropy = DecisionTreeClassifier(criterion='entropy',max_depth=i)
# Perform the cross-validation
j = 0
for train_inds, test_inds in kfold.split(X):
# Do the split
X_train = X[train_inds]
Y_train = Y[train_inds]
X_test = X[test_inds]
Y_test = Y[test_inds]
# We will scale the data
scaler = StandardScaler()
scaler.fit(X_train)
# first on full data
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
del scaler
# calculate accuracies for the k fold
tree_gini.fit(X_train, Y_train)
train_accuracy_gini_d[j]=tree_gini.score(X_train,Y_train)
test_accuracy_gini_d[j]=tree_gini.score(X_test,Y_test)
tree_entropy.fit(X_train, Y_train)
train_accuracy_entropy_d[j]=tree_entropy.score(X_train,Y_train)
test_accuracy_entropy_d[j]=tree_entropy.score(X_test,Y_test)
j += 1
#print(i/(max_degree)*100,'%')
#print(test_accuracy_gini_d,'gini',i)
#print(test_accuracy_entropy_d,'entropy',i)
#print(' ')
train_accuracy_gini[i-1]=np.mean(train_accuracy_gini_d)
test_accuracy_gini[i-1]=np.mean(test_accuracy_gini_d)
train_accuracy_entropy[i-1]=np.mean(train_accuracy_entropy_d)
test_accuracy_entropy[i-1]=np.mean(test_accuracy_entropy_d)
train_var_gini[i-1]=np.std(train_accuracy_gini_d)
test_var_gini[i-1]=np.std(test_accuracy_gini_d)
train_var_entropy[i-1]=np.std(train_accuracy_entropy_d)
test_var_entropy[i-1]=np.std(test_accuracy_entropy_d)
#Plot the accuracy for training and test data as a function of max_depth
# just uncomment the lines below to generate the plots (ctrl + /)
# fig, tree=plt.subplots()
# tree.set_xlabel('max_depth')
# tree.set_ylabel('accuracy')
# tree.plot(maxdepth,train_accuracy_gini, color='r', label='Training [Gini]' )
# tree.plot(maxdepth,test_accuracy_gini, color='r',linestyle="--", label='Test [Gini]' )
# tree.plot(maxdepth,train_accuracy_entropy, color='b', label='Training [entropy]' )
# tree.plot(maxdepth,test_accuracy_entropy, color='b',linestyle="--", label='Test [entropy]' )
# tree.legend()
# #plor the variance for training and test data as a function of max_depth
# fig, treevar=plt.subplots()
# treevar.set_xlabel('max_depth')
# treevar.set_ylabel('accuracy standard deviation')
# treevar.plot(maxdepth,train_var_gini, color='r', label='Training [Gini]' )
# treevar.plot(maxdepth,test_var_gini, color='r',linestyle="--", label='Test [Gini]' )
# treevar.plot(maxdepth,train_var_entropy, color='b', label='Training [entropy]' )
# treevar.plot(maxdepth,test_var_entropy, color='b',linestyle="--", label='Test [entropy]' )
# treevar.legend()
# plt.show()
For Random Forest, we use a larger max_depth in order to check the behavior of the accuracy of training and test data in that regime. The code used is as follows:
In [ ]:
############################################################
# Random Forest #
############################################################
from sklearn.ensemble import RandomForestClassifier
#Split data
maxdegree=40
maxdepth=np.zeros(maxdegree) # Make an array with different max_depth
np.random.seed(2030)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2, random_state = 66)
# Initialize the array for accuracy with different max_depth
train_accuracy_forest_gini=np.zeros(maxdegree)
test_accuracy_forest_gini=np.zeros(maxdegree)
train_accuracy_forest_entropy=np.zeros(maxdegree)
test_accuracy_forest_entropy=np.zeros(maxdegree)
for i in range(1,maxdegree+1):
# Random Forest Method with 100 trees
maxdepth[i-1]=i
forest_gini=RandomForestClassifier(n_estimators=100, max_depth=i)
forest_gini.fit(X_train, Y_train)
train_accuracy_forest_gini[i-1]=forest_gini.score(X_train, Y_train)
test_accuracy_forest_gini[i-1]=forest_gini.score(X_test, Y_test)
forest_entropy=RandomForestClassifier(n_estimators=100, max_depth=i)
forest_entropy.fit(X_train, Y_train)
train_accuracy_forest_entropy[i-1]=forest_entropy.score(X_train, Y_train)
test_accuracy_forest_entropy[i-1]=forest_entropy.score(X_test, Y_test)
#print(i/(maxdegree)*100,'%')
# just uncomment the lines below to generate the plots (ctrl + /)
# print("accuracy on training data with criterion gini [forest]=",np.around(train_accuracy_forest_gini,decimals=2))
# print("accuracy on test data with criterion gini [forest] =", np.around(test_accuracy_forest_gini,decimals=2))
# print("accuracy on training data with criterion entropy [forest]=",np.around(train_accuracy_forest_entropy,decimals=2))
# print("accuracy on test data with criterion entrpy [forest]=", np.around(test_accuracy_forest_entropy,decimals=2))
#Plot the accuracy for training and test data as a function of max_depth
# just uncomment the lines below to generate the plots (ctrl + /)
# fig, forest=plt.subplots()
# forest.set_xlabel('max_depth')
# forest.set_ylabel('accuracy')
# forest.set_title('Random Forest')
# forest.plot(maxdepth,train_accuracy_forest_gini, color='r', label='Training [Gini]' )
# forest.plot(maxdepth,test_accuracy_forest_gini, color='r',linestyle="--", label='Test [Gini]' )
# forest.plot(maxdepth,train_accuracy_forest_entropy, color='b', label='Training [entropy]' )
# forest.plot(maxdepth,test_accuracy_forest_entropy, color='b',linestyle="--", label='Test [entropy]' )
# forest.legend()
# CONFUSION MATRIX for gini criteria and max_depth=12
# just uncomment the lines below to generate the plots (ctrl + /)
# forest=RandomForestClassifier(n_estimators=100, max_depth=12)
# forest.fit(X_train,Y_train)
# y_pred=forest.predict(X_test)
# fig, matrix = plt.subplots(figsize = (13,10))
# sns.heatmap(confusion_matrix(Y_test,y_pred),annot=True,fmt = "d",linecolor="k",linewidths=3)
# matrix.set_xlabel('True label',fontsize=18)
# matrix.set_ylabel('Predicted label',fontsize=18)
# matrix.set_title("CONFUSION MATRIX",fontsize=20)
# plt.tick_params(labelsize=18)
# plt.show()
These quantities are used to evaluate the prediction power of our models.
[1] D.R. Lorimer and M. Kramer, "Handbook of Pulsar Astronomy", Cambridge University Press, 2005.
[2] A. Scaife, "CSC2019 - Introduction to Machine Learning", As595.github.io, 2020. [Online]. Available: https://as595.github.io/classification/. [Accessed: 07- Feb- 2020].
[3] J. Leeuwen, "Pulsar Animations", Astron.nl, 2020. [Online]. Available: https://www.astron.nl/pulsars/animations/. [Accessed: 07- Feb- 2020].
[4] R. J. Lyon, B. W. Stappers, S. Cooper, J. M. Brooke, and J. D. Knowles, "Fifty Years of Pulsar Candidate Selection: From simple filters to a new principled real-time classification approach", Monthly Notices of the Royal Astronomical Society 459 (1), 1104-1123, DOI: 10.1093/mnras/stw656
[5] R. P. Eatough et al., "Selection of radio pulsar candidates using artificial neural networks", Monthly Notices of the Royal Astronomical Society 407 (4), 2443-2450, 2010.
[6] S.D. Bates et al., "The High Time Resolution Universe Survey VI: An Artificial Neural Network and Timing of 75 Pulsars", Monthly Notices of the Royal Astronomical Society 427 (2), 1052-1065, 2012.
[7] D. Thornton, "The High Time Resolution Radio Sky", PhD Thesis, University of Manchester, School of Physics and Astronomy, 2013.
[8] K. J. Lee et al., "PEACE: Pulsar Evaluation Algorithm for Candidate Extraction - A software package for post-analysis processing of pulsar survey candidates", Monthly Notices of the Royal Astronomical Society 433 (1), 688-694, 2013.
[9] V. Morello et al., "SPINN: a straightforward machine learning solution to the pulsar candidate selection problem", Monthly Notices of the Royal Astronomical Society 443 (2), 1651-1662, 2014.
[10] A. Bird et al., "The Python Workshop: A Practical, No-Nonsense Introduction to Python Development", Packt Publishing, 2019.
[11] P. Mehta et al., "A high-bias, low-variance introduction to machine learning for physicists", Phyics Reports 810, 1-124, 2019.
[12] M. Hjorth-Jensen, "Data Analysis and Machine Learning: Neural networks, from the simple perceptron to deep learning", compphysics.github.io, 2020. [Online]. Available: https://compphysics.github.io/MLErasmus/doc/pub/NeuralNet. [Accessed: 24- Jan- 2020].
[13] M. Nielsen, "Neural Networks and Deep Learning", 2020. [Online]. Available: http://neuralnetworksanddeeplearning.com. [Accessed: 08-Feb- 2020].
[14] R. J. Lyon, HTRU2, DOI:10.6084/m9.figshare.3080389.v1.