背景
在当前的科学研究中,顶级期刊(如Nature、Science、柳叶刀等)所发表的文章不仅要求研究内容具有前沿性和创新性,同时也越来越强调研究结果的实际应用性和可复现性。近年来,随着机器学习和人工智能技术的迅猛发展,越来越多的研究者选择将他们的模型以应用程序(App)的形式进行部署,从而使审稿人和其他研究者可以通过简单的界面,输入相关数据来验证模型的性能和应用价值
这种趋势在医学领域尤为显著,例如,研究人员可以开发基于机器学习的诊断模型,并将其部署为App,审稿人只需输入病人的特征数据,App就能够返回病人患病的概率,这种应用不仅提高了研究成果的可操作性,也为医学研究中的实际诊断提供了有力支持,然而,如何在自己的研究中复现这些顶刊的App部署过程,仍然是许多研究者面临的挑战
本篇文章旨在探讨如何有效地复现顶刊中的App部署过程,包括从模型训练、优化到最终的App开发和部署,我们将深入剖析每一个关键步骤,结合实际案例,为研究者提供一条清晰的路径,以确保其研究成果能够顺利转化为实用的应用工具
相关期刊
这是几篇相关文献, 模型都被以应用程序(App)的形式进行部署,在这里我将对第三篇文献的APP进行部署,当然我没有这篇文章的原始数据,于是将采用 Heart Disease UCI数据集(有小变动),来代替接下来的所有步骤,最后部署和其功能一模一样的Heart Disease Predictor在线访问APP,APP链接如下:
https://7mmyfowsw5biqikcpmjlvd.streamlit.app/
数据特征描述
-
age
-
sex
-
cp:chest pain type(4 values) 胸痛类型
---Value 1: typical angina 典型心绞痛
---Value 2: atypical angina 非典型心绞痛
---Value 3: non-anginal pain 非心绞痛
---Value 4: asymptomatic 无症状
-
trestbps:resting blood pressure 静息血压
-
chol:serum cholestoral in mg/dl 血清胆固醇
-
fbs:fasting blood sugar >120 mg/dl 空腹血糖
-
restecg:resting electrocardiographic results 静息心电图
---Value 0:normal 正常
---Value 1:having ST-T wave abnormality ST-T波异常
(T波倒转和/或ST仰角或俯角大于0.05 mV)
---Value 2:showing probable or definite left ventricular hypertrophy by Estes' cri teria 根据Estes标准显 示可能或确定存在左心室肥大
-
thalach:maximum heart rate achieved 达到最大心率
-
exang:exercise induced angina 运动性心绞痛
-
oldpeak:ST depression induced by exercise relative to rest
运动相对于休息引起的ST段压低
-
solp:the slope of the peak exercise ST segment
运动峰ST段的坡度
-
ca:number of major vessels(0-3) colored by flourosopy
用荧光染色的主要血管数
-
thal: 1=normal; 2=fixed defect; 3 = reversable defect
1=正常;2=固定缺陷;3=可逆转缺陷
14)target:Heart disease(0 = no, 1=yes)
代码实现
数据读取并分割
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
df = pd.read_csv('Dataset.csv')
# 划分特征和目标变量
X = df.drop(['target'], axis=1)
y = df['target']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42, stratify=df['target'])
df.head()
模型构建
import xgboost as xgb
from sklearn.model_selection import GridSearchCV
# XGBoost模型参数
params_xgb = {
'learning_rate': 0.02, # 学习率,控制每一步的步长,用于防止过拟合。典型值范围:0.01 - 0.1
'booster': 'gbtree', # 提升方法,这里使用梯度提升树(Gradient Boosting Tree)
'objective': 'binary:logistic', # 损失函数,这里使用逻辑回归,用于二分类任务
'max_leaves': 127, # 每棵树的叶子节点数量,控制模型复杂度。较大值可以提高模型复杂度但可能导致过拟合
'verbosity': 1, # 控制 XGBoost 输出信息的详细程度,0表示无输出,1表示输出进度信息
'seed': 42, # 随机种子,用于重现模型的结果
'nthread': -1, # 并行运算的线程数量,-1表示使用所有可用的CPU核心
'colsample_bytree': 0.6, # 每棵树随机选择的特征比例,用于增加模型的泛化能力
'subsample': 0.7, # 每次迭代时随机选择的样本比例,用于增加模型的泛化能力
'eval_metric': 'logloss' # 评价指标,这里使用对数损失(logloss)
}
# 初始化XGBoost分类模型
model_xgb = xgb.XGBClassifier(**params_xgb)
# 定义参数网格,用于网格搜索
param_grid = {
'n_estimators': [100, 200, 300, 400, 500], # 树的数量
'max_depth': [3, 4, 5, 6, 7], # 树的深度
'learning_rate': [0.01, 0.02, 0.05, 0.1], # 学习率
}
# 使用GridSearchCV进行网格搜索和k折交叉验证
grid_search = GridSearchCV(
estimator=model_xgb,
param_grid=param_grid,
scoring='neg_log_loss', # 评价指标为负对数损失
cv=5, # 5折交叉验证
n_jobs=-1, # 并行计算
verbose=1 # 输出详细进度信息
)
# 训练模型
grid_search.fit(X_train, y_train)
# 输出最优参数
print("Best parameters found: ", grid_search.best_params_)
print("Best Log Loss score: ", -grid_search.best_score_)
# 使用最优参数训练模型
best_model = grid_search.best_estimator_
这部分代码理解参考文章 优化XGBoost分类模型:网格搜索与K折交叉验证实现
模型评价
详细指标
from sklearn.metrics import classification_report
# 预测测试集
y_pred = best_model.predict(X_test)
# 输出模型报告, 查看评价指标
print(classification_report(y_test, y_pred))
ROC曲线
from sklearn.metrics import roc_curve, auc
# 预测概率
y_score = best_model.predict_proba(X_test)[:, 1]
# 计算ROC曲线
fpr_logistic, tpr_logistic, _ = roc_curve(y_test, y_score)
roc_auc_logistic = auc(fpr_logistic, tpr_logistic)
# 绘制ROC曲线
plt.figure()
plt.plot(fpr_logistic, tpr_logistic, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc_logistic)
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()
模型保存
import joblib
# 保存模型
joblib.dump(best_model , 'XGBoost.pkl')
使用 joblib.dump 将训练好的模型 best_model 保存为名为 'XGBoost.pkl' 的文件,以便后续加载和使用,以此部署APP
Streamlit应用程序开发
import streamlit as st
import joblib
import numpy as np
import pandas as pd
import shap
import matplotlib.pyplot as plt
# Load the model
model = joblib.load('XGBoost.pkl')
# Define feature options
cp_options = {
1: 'Typical angina (1)',
2: 'Atypical angina (2)',
3: 'Non-anginal pain (3)',
4: 'Asymptomatic (4)'
}
restecg_options = {
0: 'Normal (0)',
1: 'ST-T wave abnormality (1)',
2: 'Left ventricular hypertrophy (2)'
}
slope_options = {
1: 'Upsloping (1)',
2: 'Flat (2)',
3: 'Downsloping (3)'
}
thal_options = {
1: 'Normal (1)',
2: 'Fixed defect (2)',
3: 'Reversible defect (3)'
}
# Define feature names
feature_names = [
"Age", "Sex", "Chest Pain Type", "Resting Blood Pressure", "Serum Cholesterol",
"Fasting Blood Sugar", "Resting ECG", "Max Heart Rate", "Exercise Induced Angina",
"ST Depression", "Slope", "Number of Vessels", "Thal"
]
# Streamlit user interface
st.title("Heart Disease Predictor")
# age: numerical input
age = st.number_input("Age:", min_value=1, max_value=120, value=50)
# sex: categorical selection
sex = st.selectbox("Sex (0=Female, 1=Male):", options=[0, 1], format_func=lambda x: 'Female (0)' if x == 0 else 'Male (1)')
# cp: categorical selection
cp = st.selectbox("Chest pain type:", options=list(cp_options.keys()), format_func=lambda x: cp_options[x])
# trestbps: numerical input
trestbps = st.number_input("Resting blood pressure (trestbps):", min_value=50, max_value=200, value=120)
# chol: numerical input
chol = st.number_input("Serum cholesterol in mg/dl (chol):", min_value=100, max_value=600, value=200)
# fbs: categorical selection
fbs = st.selectbox("Fasting blood sugar > 120 mg/dl (fbs):", options=[0, 1], format_func=lambda x: 'False (0)' if x == 0 else 'True (1)')
# restecg: categorical selection
restecg = st.selectbox("Resting electrocardiographic results:", options=list(restecg_options.keys()), format_func=lambda x: restecg_options[x])
# thalach: numerical input
thalach = st.number_input("Maximum heart rate achieved (thalach):", min_value=50, max_value=250, value=150)
# exang: categorical selection
exang = st.selectbox("Exercise induced angina (exang):", options=[0, 1], format_func=lambda x: 'No (0)' if x == 0 else 'Yes (1)')
# oldpeak: numerical input
oldpeak = st.number_input("ST depression induced by exercise relative to rest (oldpeak):", min_value=0.0, max_value=10.0, value=1.0)
# slope: categorical selection
slope = st.selectbox("Slope of the peak exercise ST segment (slope):", options=list(slope_options.keys()), format_func=lambda x: slope_options[x])
# ca: numerical input
ca = st.number_input("Number of major vessels colored by fluoroscopy (ca):", min_value=0, max_value=4, value=0)
# thal: categorical selection
thal = st.selectbox("Thal (thal):", options=list(thal_options.keys()), format_func=lambda x: thal_options[x])
# Process inputs and make predictions
feature_values = [age, sex, cp, trestbps, chol, fbs, restecg, thalach, exang, oldpeak, slope, ca, thal]
features = np.array([feature_values])
if st.button("Predict"):
# Predict class and probabilities
predicted_class = model.predict(features)[0]
predicted_proba = model.predict_proba(features)[0]
# Display prediction results
st.write(f"**Predicted Class:** {predicted_class}")
st.write(f"**Prediction Probabilities:** {predicted_proba}")
# Generate advice based on prediction results
probability = predicted_proba[predicted_class] * 100
if predicted_class == 1:
advice = (
f"According to our model, you have a high risk of heart disease. "
f"The model predicts that your probability of having heart disease is {probability:.1f}%. "
"While this is just an estimate, it suggests that you may be at significant risk. "
"I recommend that you consult a cardiologist as soon as possible for further evaluation and "
"to ensure you receive an accurate diagnosis and necessary treatment."
)
else:
advice = (
f"According to our model, you have a low risk of heart disease. "
f"The model predicts that your probability of not having heart disease is {probability:.1f}%. "
"However, maintaining a healthy lifestyle is still very important. "
"I recommend regular check-ups to monitor your heart health, "
"and to seek medical advice promptly if you experience any symptoms."
)
st.write(advice)
# Calculate SHAP values and display force plot
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(pd.DataFrame([feature_values], columns=feature_names))
shap.force_plot(explainer.expected_value, shap_values[0], pd.DataFrame([feature_values], columns=feature_names), matplotlib=True)
plt.savefig("shap_force_plot.png", bbox_inches='tight', dpi=1200)
st.image("shap_force_plot.png")
使用 Streamlit 构建心脏病风险预测应用程序,加载前文代码训练最好的 XGBoost 模型,接受用户输入的多种健康指标,通过模型预测用户是否存在患心脏病的风险,并给出相应的建议,此外,还使用 SHAP力图可视化来解释模型的预测结果,以此来达到论文复现
APP部署
准备项目文件
确保你的项目文件夹中包含以下文件
- Python脚本文件 (your_script.py):这是包含 Streamlit 代码的文件
- requirements.txt:列出所有依赖库的文件
上传项目到Github
如果还没有 GitHub 仓库,你需要创建一个并将项目推送到仓库中
创建 GitHub 仓库
- 登录到 GitHub
- 点击右上角的“New”按钮创建一个新仓库
- 选择仓库的名字并初始化 README 文件
- 将项目文件推送到这个仓库中,以下是推送代码的步骤(假设你已经在本地初始化了 Git)
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/yourusername/your-repo-name.git
git push -u origin main
部署到 Streamlit Cloud
treamlit Cloud 提供了一个简单的界面来托管 Streamlit 应用程序
a. 登录 Streamlit Cloud
访问 Streamlit Cloud
使用 GitHub 账户登录(因为你需要访问 GitHub 仓库)
b. 部署应用
登录后,点击页面右上角的 "Create app" 按钮
在弹出的对话框中,选择你刚刚上传到 GitHub 的项目仓库
选择分支(一般为 main)和要运行的 Python 脚本文件(如 your_script.py)
点击 "Deploy" 按钮
Streamlit Cloud 会开始构建和部署你的应用,这个过程可能需要几分钟
这就是你需要选择的界面,部署完成后就可以 访问你的应用了, Streamlit 会生成一个唯一的 URL,通过这个 URL,你和其他用户可以访问你的应用, 例如我们这里生成的URL为:
https://7mmyfowsw5biqikcpmjlvd.streamlit.app/
当然这里是一种部署APP的方法读者可以自行选择部署方法,以及相关配置
APP演示
当你访问以上这个网址时会出现这个界面Heart Disease Predictor此时你只需要输入每个特征相关的值即可,然后点击Predict按钮,APP就会自动给出模型预测结果为那个类比,每个类别的概率,相关建议以及力图,具体输出如下:
到这里就完美复现了第三篇顶刊文章的APP部署,考虑到相关读者可能对于代码上传Github,模型部署到Streamlit Cloud会存在很多问题,作者考虑会找一个机会,录制这个过程的相关视频以供读者学习,当然也支持读者定制相关APP
往期推荐
利用XGBoost模型进行多分类任务下的SHAP解释附代码讲解及GUI展示
快速选择最佳模型:轻松上手LightGBM、XGBoost、CatBoost和NGBoost!
小白轻松上手:一键生成SHAP解释图的GUI应用,支持多种梯度提升模型选择
综合多种梯度提升模型:LightGBM、XGBoost、CatBoost与NGBoost的集成预测
微信号|deep_ML
欢迎添加作者微信进入Python、ChatGPT群
进群请备注Python或AI进入相关群
无需科学上网、同步官网所有功能、使用无限制
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
个人观点,仅供参考