Ссылка на репозиторий github — https://github.com/priya-nath/Breast_Cancer_Prediction_Using_ML_Classification
Источник изображения — https://images.app.goo.gl/6vpi6R2tszq3NJUS7
Согласно статистике, около 12–13% женщин во всем мире страдают от рака молочной железы, и со временем этот показатель увеличивается. Пациент может даже умереть, если вовремя не поставить диагноз и не принять надлежащие лекарства. Врачи могут определить только на 70–80% точно, что может представлять серьезную угрозу для невыявленных пациенток, страдающих раком молочной железы. Используя машинное обучение, его можно диагностировать с точностью более 95%.
Цель проекта
Проект направлен на создание классификатора машинного обучения, который с использованием заданных параметров из набора данных предсказывает, является ли клетка злокачественной (раковой) или доброкачественной (не раковой).
Импорт различных библиотек Python
In [1]:
import io import pandas as pd import numpy as np import matplotlib.pyplot as plt %matplotlib inline import seaborn as sns /usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead. import pandas.util.testing as tm
In [2]:
from google.colab import files uploaded = files.upload() Saving Breast_cancer_Data.csv to Breast_cancer_Data.csv
Чтение набора данных
Набор данных взят с kaggle (https://www.kaggle.com/uciml/breast-cancer-wisconsin-data).
In [3]:
df = pd.read_csv(io.BytesIO(uploaded['Breast_cancer_Data.csv']))
Описание набора данных
Набор данных содержит 569 строк и 32 столбца. Некоторые столбцы описаны ниже.
id — этот номер идентификатора. назначается каждому пациенту и является уникальным.
диагностика — это будет наша целевая переменная, «M» означает злокачественную опухоль (раковую), а «B» означает доброкачественную опухоль (нераковую).
радиус — Расстояние от центра до периметра ячейки
текстура — Стандартное отклонение значений шкалы серого
perimeter_mean — Среднее значение периметра
area_mean — Среднее значение площади ячейки
гладкость — Локальное изменение в длинах радиуса
concavity — Серьезность вогнутых узоров на контуре
симметрия
fractal_dimension
Среднее значение, стандартная ошибка и наихудшие признаки были рассчитаны для каждого изображения, в результате чего было получено 30 признаков.
In [4]:
df.head()
Выход[4]:
In [5]:
df.columns
Выход[5]:
Index(['id', 'diagnosis', 'radius_mean', 'texture_mean', 'perimeter_mean', 'area_mean', 'smoothness_mean', 'compactness_mean', 'concavity_mean', 'concave points_mean', 'symmetry_mean', 'fractal_dimension_mean', 'radius_se', 'texture_se', 'perimeter_se', 'area_se', 'smoothness_se', 'compactness_se', 'concavity_se', 'concave points_se', 'symmetry_se', 'fractal_dimension_se', 'radius_worst', 'texture_worst', 'perimeter_worst', 'area_worst', 'smoothness_worst', 'compactness_worst', 'concavity_worst', 'concave points_worst', 'symmetry_worst', 'fractal_dimension_worst', 'Unnamed: 32'], dtype='object')
In [6]:
df.info() <class 'pandas.core.frame.DataFrame'> RangeIndex: 569 entries, 0 to 568 Data columns (total 33 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 569 non-null int64 1 diagnosis 569 non-null object 2 radius_mean 569 non-null float64 3 texture_mean 569 non-null float64 4 perimeter_mean 569 non-null float64 5 area_mean 569 non-null float64 6 smoothness_mean 569 non-null float64 7 compactness_mean 569 non-null float64 8 concavity_mean 569 non-null float64 9 concave points_mean 569 non-null float64 10 symmetry_mean 569 non-null float64 11 fractal_dimension_mean 569 non-null float64 12 radius_se 569 non-null float64 13 texture_se 569 non-null float64 14 perimeter_se 569 non-null float64 15 area_se 569 non-null float64 16 smoothness_se 569 non-null float64 17 compactness_se 569 non-null float64 18 concavity_se 569 non-null float64 19 concave points_se 569 non-null float64 20 symmetry_se 569 non-null float64 21 fractal_dimension_se 569 non-null float64 22 radius_worst 569 non-null float64 23 texture_worst 569 non-null float64 24 perimeter_worst 569 non-null float64 25 area_worst 569 non-null float64 26 smoothness_worst 569 non-null float64 27 compactness_worst 569 non-null float64 28 concavity_worst 569 non-null float64 29 concave points_worst 569 non-null float64 30 symmetry_worst 569 non-null float64 31 fractal_dimension_worst 569 non-null float64 32 Unnamed: 32 0 non-null float64 dtypes: float64(31), int64(1), object(1) memory usage: 146.8+ KB
In [7]:
df.describe()
Выход[7]:
In [8]:
df.isnull().sum()
Выход[8]:
id 0 diagnosis 0 radius_mean 0 texture_mean 0 perimeter_mean 0 area_mean 0 smoothness_mean 0 compactness_mean 0 concavity_mean 0 concave points_mean 0 symmetry_mean 0 fractal_dimension_mean 0 radius_se 0 texture_se 0 perimeter_se 0 area_se 0 smoothness_se 0 compactness_se 0 concavity_se 0 concave points_se 0 symmetry_se 0 fractal_dimension_se 0 radius_worst 0 texture_worst 0 perimeter_worst 0 area_worst 0 smoothness_worst 0 compactness_worst 0 concavity_worst 0 concave points_worst 0 symmetry_worst 0 fractal_dimension_worst 0 Unnamed: 32 569 dtype: int64
Замечено, что только 1 столбец имеет все записи NULL. Этот столбец удален.
In [9]:
df = df.drop(['Unnamed: 32'], axis = 1)
Визуализация данных
Можно заметить, что 62,7% (357 из 569) людей имели доброкачественные опухоли (нераковые) и 37,3% (212 из 569) из них имели злокачественные опухоли (раковые).
In [10]:
df['diagnosis'].value_counts()
Выход[10]:
B 357 M 212 Name: diagnosis, dtype: int64
In [11]:
f,ax=plt.subplots(1,2,figsize=(18,8)) df['diagnosis'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.1f%%',ax=ax[0],shadow=True) ax[0].set_title('diagnosis') ax[0].set_ylabel('') sns.countplot('diagnosis',data=df,ax=ax[1]) ax[1].set_title('diagnosis') plt.show() # M is for malignant, B is for benign
Построен парный график всех некоторых соответствующих функций, который визуализирует взаимосвязь между ними.
In [12]:
#pairplot of a sample of features sns.set() cols = ['radius_mean', 'texture_mean', 'perimeter_mean', 'area_mean', 'smoothness_mean', 'compactness_mean', 'concavity_mean', 'diagnosis'] sns.pairplot(df[cols], hue = 'diagnosis', height = 2.5) plt.show();
Синие обозначают злокачественную опухоль, а оранжевые - доброкачественную опухоль.
In [13]:
# Histogram betweeen radius_mean and diagnosis g=sns.FacetGrid(df, col = 'diagnosis') g.map(plt.hist,'radius_mean', bins = 20)
Исход[13]:
Теперь категориальные признаки, т. е. B (доброкачественные) и M (раковые), сопоставляются от 0 до 1 соответственно.
In [14]:
mapping = {'B':0, 'M':1} df['diagnosis'] = df['diagnosis'].map(mapping)
Матрица корреляции с использованием тепловой карты
In [15]:
#correlation matrix corrmat = df.corr() f, ax = plt.subplots(figsize=(40, 30)) sns.heatmap(corrmat, vmax=.8, square=True, annot = True, cmap = 'Spectral');
Гистограмма корреляции
Показана корреляция между различными переменными и целью.
In [16]:
#a new dataframe is created dropping our target variable df1 = df.drop(['diagnosis'], axis = 1)
Существует положительная корреляция между диагностированным доброкачественным состоянием и «ошибкой_гладкости», очень слабая положительная корреляция с «среднее_фрактальное_размерность», «ошибка_текстуры» и «ошибка_симметрии». Все остальные факторы показывают отрицательную корреляцию с диагнозом «доброкачественный» (0).
In [17]:
# visualize correlation barplot plt.figure(figsize = (16,5)) ax = sns.barplot(df1.corrwith(df.diagnosis).index, df1.corrwith(df.diagnosis)) ax.tick_params(labelrotation = 90)
Предварительная обработка данных
In [18]:
df_new = df
Менее коррелированные значения отбрасываются, чтобы получить лучший набор данных.
In [19]:
df_new = df_new.drop(['id','fractal_dimension_mean','texture_se','smoothness_se','symmetry_se','fractal_dimension_se'],axis = 1)
Нормализация некоторых входных переменных
Целью нормализации/преобразования Гаусса является приведение значений числовых столбцов в наборе данных к общему масштабу, не искажая различий в диапазонах значений. Некоторые модели машинного обучения, такие как линейная и логистическая регрессия, предполагают, что переменные нормально распределены. Если переменная не имеет нормального распределения, ее можно преобразовать с помощью некоторых методов преобразования Гаусса.
In [20]:
from scipy.stats import norm from scipy import stats def diagnostic_plots(df_new, variable):## defining a function to plot histogram and Q-Q plot plt.figure(figsize = (15,6)) plt.subplot(1,2,1) sns.distplot(df_new[variable], fit=norm); plt.subplot(1,2,2) stats.probplot(df_new[variable], dist = 'norm', plot = plt) plt.show()
Гистограмму и график QQ различных функций можно визуализировать, как показано ниже. Признаки, которые требуют нормализации, чтобы соответствовать распределению Гаусса, преобразуются с использованием логарифмического преобразования.
In [21]:
diagnostic_plots(df_new, 'radius_mean') diagnostic_plots(df_new, 'texture_mean') diagnostic_plots(df_new, 'perimeter_mean') diagnostic_plots(df_new, 'area_mean') diagnostic_plots(df_new, 'smoothness_mean') diagnostic_plots(df_new, 'area_worst') diagnostic_plots(df_new, 'perimeter_worst') diagnostic_plots(df_new, 'radius_worst') diagnostic_plots(df_new, 'texture_worst') diagnostic_plots(df_new, 'perimeter_se')
In [22]:
#applying log transformation to convert radius_mean into a gaussian distribution df_new['radius_mean'] = np.log(df_new['radius_mean'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'radius_mean') #applying log transformation to convert texture_mean into a gaussian distribution ##df_new['texture_mean'] = np.log(df_new['texture_mean'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log ##diagnostic_plots(df_new, 'texture_mean') #applying log transformation df_new['perimeter_mean'] = np.log(df_new['perimeter_mean'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'perimeter_mean') #applying log transformation df_new['area_mean'] = np.log(df_new['area_mean'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'area_mean') #applying log transformation df_new['area_worst'] = np.log(df_new['area_worst'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'area_worst') #applying log transformation df_new['perimeter_worst'] = np.log(df_new['perimeter_worst'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'perimeter_worst') #applying log transformation df_new['radius_worst'] = np.log(df_new['radius_worst'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'radius_worst') #applying log transformation df_new['texture_worst'] = np.log(df_new['texture_worst'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'texture_worst') #applying log transformation df_new['perimeter_se'] = np.log(df_new['perimeter_se'] + 1)# +1 is added in case there is any 0 input to it which would create issue in taking log diagnostic_plots(df_new, 'perimeter_se')
После логарифмического преобразования график Q-Q для этих величин больше похож на прямую линию. Следовательно, эти переменные нормированы.
Выбор функции
Я использовал ExtraTreesClassifier из модуля обучения scikit, который представляет собой классификатор на основе дерева и может использоваться для вычисления важности функций на основе примесей, которые, в свою очередь, могут использоваться для отбрасывания ненужных функций.
In [23]:
y = df_new['diagnosis'] ### Our target variable X = df_new.drop(['diagnosis'], axis = 1) ### Input features y.head(3)
Вышли[23]:
0 1 1 1 2 1 Name: diagnosis, dtype: int64
In [24]:
###splitting dataset into train and test sets with train to test set ratio as 80:20 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=42)
Принимая во внимание 25 наиболее важных функций, визуализация показана ниже:
In [26]:
from sklearn.ensemble import ExtraTreesClassifier model = ExtraTreesClassifier() model.fit(X,y) print(model.feature_importances_) feat_imp = pd.Series(model.feature_importances_, index = X.columns) feat_imp.nlargest(25).plot(kind = 'barh') plt.show() [0.06236089 0.01664255 0.06431599 0.06131801 0.00779287 0.01805748 0.05806302 0.07369793 0.00736599 0.02505073 0.03310965 0.03901086 0.00898383 0.0088296 0.00887307 0.07699415 0.02399726 0.09562853 0.09071223 0.02535813 0.03152734 0.03820249 0.09576173 0.01724264 0.01110302]
Удаление менее релевантных функций для получения лучшего набора данных.
In [27]:
df_new = df_new.drop(['compactness_se','smoothness_mean','concavity_se','symmetry_mean',\ 'fractal_dimension_worst','texture_mean'],axis = 1)
In [28]:
y = df_new['diagnosis'] ### Our target variable X = df_new.drop(['diagnosis'], axis = 1) ### Input features y.head(3)
Вышли[28]:
0 1 1 1 2 1 Name: diagnosis, dtype: int64
In [30]:
# Feature scaling from sklearn.preprocessing import StandardScaler sc = StandardScaler() X_train_sc = sc.fit_transform(X_train) X_test_sc = sc.transform(X_test)
Стандартизация преобразует данные, чтобы иметь среднее значение, равное нулю, и стандартное отклонение, равное 1, что улучшает производительность линейной модели. Такие модели, как логистическая регрессия, машина опорных векторов, K ближайших соседей, демонстрируют улучшенные характеристики модели, тогда как модель на основе дерева и методы ансамбля не требуют выполнения масштабирования признаков, поскольку они не чувствительны к дисперсии данных.
Моделирование
In [29]:
###splitting dataset into train and test sets with train to test set ratio as 80:20 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=42)
In [31]:
from sklearn.metrics import accuracy_score
In [32]:
#logistic regression from sklearn.linear_model import LogisticRegression logreg = LogisticRegression() logreg.fit(X_train, y_train) Y_pred_log = logreg.predict(X_test) accu_reg = accuracy_score(y_test, Y_pred_log) print("Accuracy score using Logistic Regression:", accu_reg*100) Accuracy score using Logistic Regression: 96.49122807017544 /usr/local/lib/python3.6/dist-packages/sklearn/linear_model/_logistic.py:940: ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT. Increase the number of iterations (max_iter) or scale the data as shown in: https://scikit-learn.org/stable/modules/preprocessing.html Please also refer to the documentation for alternative solver options: https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression extra_warning_msg=_LOGISTIC_SOLVER_CONVERGENCE_MSG)
In [33]:
#logistic regression with feature scaling from sklearn.linear_model import LogisticRegression logreg = LogisticRegression() logreg.fit(X_train_sc, y_train) Y_pred_log = logreg.predict(X_test_sc) accu_reg = accuracy_score(y_test, Y_pred_log) print("Accuracy score using Logistic Regression with feature scaling:", accu_reg*100) Accuracy score using Logistic Regression with feature scaling: 99.12280701754386
In [34]:
#support vector machine from sklearn.svm import SVC svc = SVC() svc.fit(X_train, y_train) Y_pred_svm = svc.predict(X_test) accu_svc = accuracy_score(y_test, Y_pred_svm) print("Accuracy score using Support Vector Machine:", accu_svc*100) Accuracy score using Support Vector Machine: 92.10526315789474
In [35]:
#support vector machine with feature scaling from sklearn.svm import SVC svc = SVC() svc.fit(X_train_sc, y_train) Y_pred_svm = svc.predict(X_test_sc) accu_svc = accuracy_score(y_test, Y_pred_svm) print("Accuracy score using Support Vector Machine with feature scaling:", accu_svc*100) Accuracy score using Support Vector Machine with feature scaling: 96.49122807017544
In [36]:
#knn from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors = 3) knn.fit(X_train, y_train) Y_pred_knn = svc.predict(X_test) accu_knn = accuracy_score(y_test, Y_pred_knn) print("Accuracy score using K nearest neighbours", accu_knn*100) Accuracy score using K nearest neighbours 37.719298245614034
In [37]:
#knn with feature scaling from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors = 3) knn.fit(X_train_sc, y_train) Y_pred_knn = svc.predict(X_test_sc) accu_knn = accuracy_score(y_test, Y_pred_knn) print("Accuracy score using K nearest neighbours with feature scaling:", accu_knn*100) Accuracy score using K nearest neighbours with feature scaling: 96.49122807017544
In [38]:
# Gaussian Naive Bayes from sklearn.naive_bayes import GaussianNB gaussian = GaussianNB() gaussian.fit(X_train, y_train) Y_pred_nb = gaussian.predict(X_test) acc_gaussian = accuracy_score(y_test, Y_pred_nb) print("Accuracy score using Gaussian Naive Bayes:", acc_gaussian*100) Accuracy score using Gaussian Naive Bayes: 95.6140350877193
In [39]:
# Gaussian Naive Bayes with feature scaling from sklearn.naive_bayes import GaussianNB gaussian = GaussianNB() gaussian.fit(X_train_sc, y_train) Y_pred_nb = gaussian.predict(X_test_sc) acc_gaussian = accuracy_score(y_test, Y_pred_nb) print("Accuracy score using Gausian Naive Bayes with feature scaling:", acc_gaussian*100) Accuracy score using Gausian Naive Bayes with feature scaling: 95.6140350877193
In [40]:
# Decision Tree from sklearn.tree import DecisionTreeClassifier decision_tree = DecisionTreeClassifier() decision_tree.fit(X_train, y_train) Y_pred_dt = decision_tree.predict(X_test) acc_decision_tree = accuracy_score(y_test, Y_pred_dt) print("Accuracy score using Decision Tree:", acc_decision_tree*100) Accuracy score using Decision Tree: 91.22807017543859
In [41]:
# Decision Tree with feature scaling from sklearn.tree import DecisionTreeClassifier decision_tree = DecisionTreeClassifier() decision_tree.fit(X_train_sc, y_train) Y_pred_dt = decision_tree.predict(X_test_sc) acc_decision_tree = accuracy_score(y_test, Y_pred_dt) print("Accuracy score using Decision Tree with feature scaling:", acc_decision_tree*100) Accuracy score using Decision Tree with feature scaling: 91.22807017543859
In [42]:
# Random Forest from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score random_forest = RandomForestClassifier(n_estimators=100) random_forest.fit(X_train, y_train) Y_pred_rf = random_forest.predict(X_test) random_forest.score(X_train, y_train) acc_random_forest = accuracy_score(y_test, Y_pred_rf) print("Accuracy score using Random Forest:", acc_random_forest*100) Accuracy score using Random Forest: 96.49122807017544
In [43]:
# Random Forest with feature scaling from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score random_forest = RandomForestClassifier(n_estimators=100) random_forest.fit(X_train_sc, y_train) Y_pred_rf = random_forest.predict(X_test_sc) random_forest.score(X_train, y_train) acc_random_forest = accuracy_score(y_test, Y_pred_rf) print("Accuracy score using Random Forest with feature scaling:", acc_random_forest*100) Accuracy score using Random Forest with feature scaling: 95.6140350877193
In [44]:
# XGBoost Classifier from xgboost import XGBClassifier xgb_classifier = XGBClassifier() xgb_classifier.fit(X_train, y_train) y_pred_xgb = xgb_classifier.predict(X_test) acc_xgb = accuracy_score(y_test, y_pred_xgb) print("Accuracy score using XG Boost Classifier:", acc_xgb*100) Accuracy score using XG Boost Classifier: 96.49122807017544
In [46]:
# XGBoost Classifier from xgboost import XGBClassifier xgb_classifier = XGBClassifier() xgb_classifier.fit(X_train_sc, y_train) y_pred_xgb = xgb_classifier.predict(X_test_sc) acc_xgb = accuracy_score(y_test, y_pred_xgb) print("Accuracy score using XG Boost Classifier with feature scaling:", acc_xgb*100) Accuracy score using XG Boost Classifier with feature scaling: 96.49122807017544
In [47]:
models = pd.DataFrame({ 'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression', 'Decision Tree', 'Random Forest', 'Naive Bayes', 'XG Boost'], 'Score': [accu_svc, accu_knn, accu_reg, acc_decision_tree, acc_random_forest, acc_gaussian, acc_xgb]}) models.sort_values(by='Score', ascending=False)
Вышел[47]:
Логистическая регрессия показывает самую высокую точность 99,12%. Поэтому мы будем использовать его в качестве нашей модели машинного обучения.
Матрица путаницы
Оценка матрицы путаницы имеет ошибку типа I как 0 и ошибку типа II как 1, что указывает на хорошую производительность модели.
In [48]:
from sklearn.metrics import confusion_matrix cm = confusion_matrix(y_test, Y_pred_log) plt.title('Heatmap of Confusion Matrix', fontsize = 15) sns.heatmap(cm, annot = True) plt.show()
In [49]:
from sklearn.metrics import classification_report print(classification_report(y_test, Y_pred_log)) precision recall f1-score support 0 0.99 1.00 0.99 71 1 1.00 0.98 0.99 43 accuracy 0.99 114 macro avg 0.99 0.99 0.99 114 weighted avg 0.99 0.99 0.99 114
Перекрестная проверка модели ML
Перекрестная проверка — это метод, используемый для проверки эффективности модели машинного обучения, а также процедура повторной выборки, используемая для оценки модели, если у нас есть ограниченные данные. Здесь используется 10-кратная перекрестная проверка, которая дала меньшую точность, чем точность, полученная из модели логистической регрессии. Это указывает на то, что в модели есть переобучение, которое можно преодолеть, используя больший набор данных.
In [57]:
# Cross validation from sklearn.model_selection import cross_val_score cross_validation = cross_val_score(estimator = logreg, X = X_train_sc, y = y_train, cv = 20) print("Cross validation of Logistic Regression model = ",cross_validation) print("Cross validation of Logistic Regression model (in mean) = ",cross_validation.mean()) Cross validation of Logistic Regression model = [1. 0.95652174 1. 0.95652174 1. 0.95652174 0.95652174 0.91304348 0.95652174 1. 1. 1. 1. 1. 1. 0.90909091 1. 0.95454545 0.90909091 0.95454545] Cross validation of Logistic Regression model (in mean) = 0.9711462450592885
Среднее значение точности перекрестной проверки составляет 97,11 %, а точность модели логистической регрессии — 99,12 %. Это показывает, что логистическая регрессия немного переобучена, но когда обучающих данных будет больше, это будет обобщенная модель.
Следовательно, модель может служить нашей цели, поскольку она имеет показатель точности более 95% для прогнозирования рака молочной железы у пациентов.
Спасибо, что дочитали до конца.
Ссылка на репозиторий github — https://github.com/priya-nath/Breast_Cancer_Prediction_Using_ML_Classification