演算法集錦(3)|採用醫療數據預測糖尿病的演算法

更多AI資訊,關註:九三智能控

糖尿病是一組以高血糖為特徵的代謝性疾病。糖尿病時長期存在的高血糖,導致各種組織,特別是眼、腎、心臟、血管、神經的慢性損害、功能障礙。本文將介紹如何利用機器學習與醫療數據來預測個人患糖尿病的演算法,在此過程中,我們還會學習如何進行數據準備、數據清洗、特徵選擇、模型選擇盒模型計算。

計算條件:

  • Python 3.+
  • Anaconda(Scikit Learn, Numpy, Pandas, Matplotlib, Seaborn)
  • Jupyter Notebook
  • 對監督機器學習的基本理解,特別是分類。

步驟1:數據集準備

準備數據集是一件很枯燥的事情,即使有大量的數據,有時也很難找到一個適用於待解決問題的數據集。本文中,我們直接使用UCI機器學習庫中的「Pima Indians Diabetes Database」,我們將用機器學習演算法來處理它。

步驟2:數據分析

導入數據後,首先我們應該對數據集進行分析,從而更好的理解數據和數據集的特徵,以便確定是否進行數據清理。

首先,導入必要的計算庫。

%matplotlib inlineimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsdiabetes = pd.read_csv(datasets/diabetes.csv)diabetes.columns

然後,利用pandas的head()方法來檢查資料庫。

diabetes.head()

數據集的維度用以下方法獲得。

print("Diabetes data set dimensions : {}".format(diabetes.shape))

數據集的維度顯示為(768,9)。「Outcome」列標識病人是否患有糖尿病,1代表患病,0代表健康。我們可以計算出768人中,500個標識為0(未患病),268個標識為1(患病)。

diabetes.groupby(Outcome).size()

下面,我們利用pandas的數據可視化模塊對數據集進行分析,查看數據的分布特徵。

diabetes.groupby(『Outcome』).hist(figsize=(9, 9))

步驟3:數據清理

數據清理過程中,需要考慮一下幾個方面。

(1)重複或無關的數據

(2)錯誤標識的數據,或者多次出現相同的標識

(3)缺失或空的數據點

(4)異常值

因為我們使用的是標準資料庫,所以可以假定第(1)、(2)條已經被處理過了。所以我們重點考察缺失的數據點和異常值。

缺失或空數據點

可以採用如下的函數來搜尋缺失或空缺的數據點。

diabetes.isnull().sum()diabetes.isna().sum()

結果並未發現空缺數據點,如果存在的化,我們就需要對其進行相應的處理。

異常值

分析直方圖時,我們發現某些列存在一些異常值,所以需要進行深入分析並確定如何處理它們。

血壓(Blood pressure):通過分析數據,我們發現有些血壓值為0。很明顯,一個正常人的血壓不可能為0,所以這些數據是錯誤的。

print("Total : ", diabetes[diabetes.BloodPressure == 0].shape[0])Total : 35print(diabetes[diabetes.BloodPressure == 0].groupby(Outcome)[Age].count())Outcome0 191 16Name: Age, dtype: int64

血糖水平(Plasma glucose levels): 同樣,人的血糖水平也不可能降至0,所以數據集中有5列血糖值異常。

print("Total : ", diabetes[diabetes.Glucose == 0].shape[0])Total : 5print(diabetes[diabetes.Glucose == 0].groupby(Outcome)[Age].count())Total : 5Outcome0 31 2Name: Age, dtype: int64

皮褶厚度(Skin Fold Thickness): 正常人的皮褶厚度一般不會小於10mm,可以發現該參數共出現227次0值。

print("Total : ", diabetes[diabetes.SkinThickness == 0].shape[0])Total : 227print(diabetes[diabetes.SkinThickness == 0].groupby(Outcome)[Age].count())Outcome0 1391 88Name: Age, dtype: int64

身體質量指數(BMI): 除非一個人體重降低到威脅生命的地步,否則BMI值不為0或者接近0。

print("Total : ", diabetes[diabetes.BMI == 0].shape[0])Total : 11print(diabetes[diabetes.BMI == 0].groupby(Outcome)[Age].count())Outcome0 91 2Name: Age, dtype: int64

胰島素(Insulin): 在極特殊的情況下,人體胰島素值才會降低到0,而我們發現共有374項數據的胰島素值出現0值,這顯然是不正常的。

print("Total : ", diabetes[diabetes.Insulin == 0].shape[0])Total : 374print(diabetes[diabetes.Insulin == 0].groupby(Outcome)[Age].count())Outcome0 2361 138Name: Age, dtype: int64

對於以上的異常數據,可以採用以下幾種方法進行處理:

  1. 移除異常值:通常該方法難以實現,因為移除數據意味著會丟失有價值的數據。本例中,皮褶厚度和胰島素兩列出現了大量的異常值,若移除它們,則會丟失其他列的有效數據。
  2. 採用平均值: 該方法對於某些數據集是適用的,但對於本例來說,對血壓項設置為平均值會給模型引入較大的誤差。
  3. 棄用特徵: 對於出現大量異常值的特徵,有時可考慮棄用該特徵(如皮褶厚度),但通過較難判斷是否會影響模型的準確性。

通過分析數據,我們可以得知採用的數據集並不完整。經過綜合分析,因為本例僅是為了驗證演算法的可行性,所以我們決定移除血壓、BMI和血糖各特徵中為0值的行。

diabetes_mod = diabetes[(diabetes.BloodPressure != 0) & (diabetes.BMI != 0) & (diabetes.Glucose != 0)]print(diabetes_mod.shape)(724, 9)

步驟4:特徵工程

特徵過程是指將數據轉換成特徵,從而更好的去建模、優化和提高準確率的過程。

對於本文的算例,我們採用的是成熟的數據集,無法進一步的創建或消除任何數據點了,所以我們選擇以下特徵創建模型。

『Pregnancies』, 『Glucose』, 『Blood Pressure』, 『Skin Thickness』, 『Insulin』, 『BMI』, 『Diabetes Pedigree Function』, 『Age』

粗略的看來,皮褶厚度對於預測糖尿病可能不是一個預測參數,但其未必對模型的預測結果沒有作用。所以我們採用了數據集全部的特徵,並將其設置為「X變數」,而將預測結果設置為「Y變數」。

feature_names = [Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age]X = diabetes_mod[feature_names]y = diabetes_mod.Outcome

通常,特徵工程會在選擇模型前實施。但本文中,我們採取一個不同的策略,我們先將數據集中所有的特徵放入模型中,後續再詳細的討論各個特徵對於模型的重要性。

步驟5:模型選擇

模型選擇或演算法選擇是機器學習中最有趣和最核心的部分。在該環節,我們會選擇出對數據集表現最好的模型(演算法)來進行預測。

我們會計算不同分類模型(在默認參數下)的分類準確率(或測試準確率),從而確定對數據集擬合最優的模型。首先,我們導入7種不同的分類器,分別為:K-Nearest Neighbors, Support Vector Classifier, Logistic Regression, Gaussian Naive Bayes, Random Forest and Gradient Boost。

from sklearn.neighbors import KNeighborsClassifierfrom sklearn.svm import SVCfrom sklearn.linear_model import LogisticRegressionfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.naive_bayes import GaussianNBfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.ensemble import GradientBoostingClassifier

然後,將各個分類器按默認參數初始化,並建立一個模型列表。

models = []models.append((KNN, KNeighborsClassifier()))models.append((SVC, SVC()))models.append((LR, LogisticRegression()))models.append((DT, DecisionTreeClassifier()))models.append((GNB, GaussianNB()))models.append((RF, RandomForestClassifier()))models.append((GB, GradientBoostingClassifier()))

計算方法

為了避免過擬合的發生,我們通常開展以下兩方面的工作。

  1. 訓練/測試數據劃分
  2. K摺疊交叉驗證(K-Fold Cross Validation)

這裡,我們將用「train_test_split」函數進行數據劃分,「cross_val_score」函數進行K摺疊交叉驗證。通過這兩個步驟,我們可以確定適用於數據集的最優分類器模型。

from sklearn.model_selection import train_test_splitfrom sklearn.model_selection import cross_val_scorefrom sklearn.metrics import accuracy_score

訓練/測試數據劃分

通過劃分,我們將數據集分為兩個部分,訓練數據集(Training set)和測試數據集(Testing set)。訓練數據集用來訓練模型,測試數據集用來評估模型的準確率。

代碼如下:

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify = diabetes_mod.Outcome, random_state=0)

然後我們用「accuracy_score」來計算各個模型的準確率。

names = []scores = []for name, model in models: model.fit(X_train, y_train) y_pred = model.predict(X_test) scores.append(accuracy_score(y_test, y_pred)) names.append(name)tr_split = pd.DataFrame({Name: names, Score: scores})print(tr_split)

K摺疊交叉驗證

對於個分類或回歸問題,假設有多個可選的模型為。K-摺疊交叉驗證就是將訓練集的1/k作為測試集,每個模型訓練k次,測試k次,錯誤率為k次的平均,最終選擇平均率最小的模型Mi。

該過程可以利用 Sickit_Learn庫的如下代碼實現:

names = []scores = []for name, model in models: kfold = KFold(n_splits=10, random_state=10) score = cross_val_score(model, X, y, cv=kfold, scoring=accuracy).mean() names.append(name) scores.append(score)kf_cross_val = pd.DataFrame({Name: names, Score: scores})print(kf_cross_val)

各模型的準確率如下圖所示。

axis = sns.barplot(x = Name, y = Score, data = kf_cross_val)axis.set(xlabel=Classifier, ylabel=Accuracy)for p in axis.patches: height = p.get_height() axis.text(p.get_x() + p.get_width()/2, height + 0.005, {:1.4f}.format(height), ha="center") plt.show()

可以看到,二元回歸演算法(Logistic Regression)的準確率最高(77.64%),因此我們選擇二元回歸作為數據集的模型。

步驟6:模型參數調整

採用默認參數,二元回歸模型獲得了較好的預測準確率。接下來,我們將對模型的參數進行調整,優化模型,從而獲得更準確的模型。本例採用的是GridSearchCV方法,該方法通過交叉驗證對參數空間進行求解,尋找最佳的參數。

首先,導入GridSearchCV方法。

from sklearn.model_selection import GridSearchCV

然後,給出二元回歸模型的參數列表。

# Specify parametersc_values = list(np.arange(1, 10))param_grid = [ {C: c_values, penalty: [l1], solver : [liblinear], multi_class : [ovr]}, {C: c_values, penalty: [l2], solver : [liblinear, newton-cg, lbfgs], multi_class : [ovr]}]

將數據輸入GridSearchCV,通過交叉驗證來確認不同參數的組合效果。

grid = GridSearchCV(LogisticRegression(), param_grid, cv=strat_k_fold, scoring=accuracy)grid.fit(X_new, y)

經過一系列的訓練和評估,GridSearchCV給出了一些有用的信息用來尋找最優參數。

print(grid.best_params_)print(grid.best_estimator_)

經過分析,我們確定最優的超參數如下:

{C: 1, multi_class: ovr, penalty: l2, solver: liblinear}

將優化後的參數輸入回二元回歸模型,可以看到模型預測的準確度得到了提升。

logreg_new = LogisticRegression(C=1, multi_class=ovr, penalty=l2, solver=liblinear)initial_score = cross_val_score(logreg_new, X_new, y, cv=strat_k_fold, scoring=accuracy).mean()print("Final accuracy : {} ".format(initial_score))Final accuracy : 0.7805877119643279

值得指出的是,本算例採用的是傳統的機器學習演算法,所以獲得的預測準確率(78.05%)不盡理想。若採用深度神經網路模型,預測準確率應有較大的提升,後續文章會對此進行報道。


微信群&商業合作:

  • 加入微信群:不定期分享資料,拓展行業人脈請在公眾號留言:「微信號+名字+研究領域/專業/學校/公司」,我們將很快與您聯繫。
  • 投稿(無稿費)、商業合作請留言聯繫。

weixin.qq.com/r/AC91bd- (二維碼自動識別)

推薦閱讀:

植入式醫療裝置:承載希望卻蘊含危機
人工智慧與機器學習技術在醫療保健行業中的應用

TAG:AI技術 | 智能醫療 | 糖尿病 |