1. 项目概述:当AI成为“黑盒”,我们如何让医生信任它?
在医疗领域,AI模型的每一次预测都关乎生命健康,其决策过程必须是透明、可信的。几年前,我参与一个肺部CT影像的辅助诊断项目,模型准确率高达95%,但当我们将结果呈现给放射科医生时,他们问的第一个问题不是“准确率多少”,而是“为什么模型认为这个结节是恶性的?” 我们无法给出一个直观、符合医学逻辑的解释,项目最终卡在了临床落地前的最后一公里。这正是“可解释人工智能”在医疗诊断中的核心价值所在——它不仅是技术需求,更是临床信任的基石。
“可解释AI在医疗诊断中的应用”这个项目,其核心目标就是构建一个不仅准确,而且其决策逻辑能被人类专家理解和验证的诊断辅助系统。我们不再满足于输入数据、输出结果的“黑盒”,而是要打开这个盒子,用医生能看懂的语言——比如“这个结节被判定为高风险,主要是因为其边缘毛糙、内部密度不均,这与您关注的恶性特征A和B高度吻合”——来呈现AI的思考过程。本次实践,我们将聚焦于一个经典且稳健的机器学习模型随机森林,并运用当前业界最主流的两种模型解释工具LIME和SHAP,来完整演示如何为一个医疗诊断模型赋予“解释力”。无论你是数据科学家、医学研究员,还是对AI医疗应用感兴趣的开发者,这篇内容都将带你从理论到代码,亲手构建一个可解释的诊断流程,理解每一个预测背后的“为什么”。
2. 核心思路与技术选型:为什么是随机森林、LIME与SHAP?
在开始敲代码之前,我们必须理清技术栈选择的底层逻辑。医疗数据通常具有高维度、小样本、非线性和特征间存在复杂交互的特点,同时,解释必须满足局部精确性和全局一致性。
2.1 基模型:为何选择随机森林?
在众多机器学习算法中,我们选择随机森林作为基模型,主要基于其在医疗诊断场景下的四大优势:
- 高精度与鲁棒性:随机森林通过集成大量决策树,有效降低了单棵树的过拟合风险,对数据中的噪声和缺失值不敏感。在医疗数据集中,样本量有限且质量参差不齐是常态,随机森林的鲁棒性至关重要。
- 天然的特征重要性度量:在训练过程中,随机森林可以基于基尼不纯度或信息增益的减少量,计算出每个特征对于模型预测的整体贡献度(即特征重要性)。这为我们提供了一种全局的、模型层面的解释,能快速回答“在所有预测中,哪些特征总体上最重要?”。
- 处理非线性关系的能力:疾病与生物标志物之间的关系极少是简单的线性关系。决策树本身就能捕捉复杂的非线性模式和特征交互,随机森林继承了这一能力,非常适合医疗数据的复杂性。
- 较少的预处理需求:与深度学习或某些线性模型相比,随机森林对数据的标准化、归一化要求较低,能同时处理数值型和类别型特征,降低了工程复杂度。
注意:随机森林的“解释”是整体层面的,它告诉我们特征的重要性排序,但无法针对单个特定患者的预测给出详细理由。例如,我们知道“血糖水平”在整个糖尿病预测模型中很重要,但无法说明为什么模型对张三的预测是“患病”,而对李四的预测是“健康”。这就需要局部解释方法。
2.2 解释器:LIME与SHAP的互补哲学
为了解释单个预测,我们引入了LIME和SHAP。它们代表了两种不同的解释哲学,在实践中互补。
LIME:局部可解释的模型无关解释
- 核心思想:对于一个复杂的模型预测(如随机森林对某个患者的诊断),LIME不去解释整个复杂模型,而是在这个预测点附近局部地构建一个简单的、可解释的“代理模型”(通常是线性模型或决策树)。它通过扰动输入特征(例如,轻微改变患者的某项化验指标),观察复杂模型输出的变化,然后用一个简单的模型去拟合这种变化关系。
- 优点:高度灵活,与模型无关。你可以用它解释任何模型(随机森林、神经网络、SVM等)。它的解释非常直观,例如:“将患者A的‘淋巴细胞计数’从5.0提升到5.5,模型将其患流感的风险从70%降低到了65%”。
- 局限:解释依赖于对输入数据的扰动方式,可能存在稳定性问题(多次运行结果略有差异)。其解释严格局限于单个预测点附近,缺乏全局视角。
SHAP:基于博弈论的统一解释框架
- 核心思想:SHAP源于博弈论的Shapley值,为每个特征对于某个特定预测的贡献分配一个数值。其核心问题是:“当这个特征存在时,与它不存在时相比,模型的预测值改变了多少?” SHAP值就是对这个贡献的公平分配。
- 优点:具有坚实的数学理论基础,满足一致性、局部准确性等良好性质。SHAP能同时提供局部解释(单个预测的特征贡献)和全局解释(所有预测中特征的总体影响模式)。例如,SHAP可以告诉我们,对于患者A,“高龄”这个特征将其患病风险提升了15个百分点,而“规律运动”这个特征将其风险降低了8个百分点。
- 局限:计算成本通常高于LIME,尤其是对于树模型以外的复杂模型。虽然SHAP有专门针对树模型的优化算法(TreeSHAP,速度极快),但其思想理解起来比LIME稍显抽象。
我们的策略:在本项目中,我们将利用随机森林的全局特征重要性进行初步的特征筛选和医学意义验证。然后,针对关键病例(如模型预测与医生初诊不一致的病例),同时使用LIME和SHAP进行深入的局部解释,对比两种方法的结果,相互验证,生成一份让临床医生信服的“AI诊断报告”。
3. 实战环境搭建与数据准备
理论清晰后,我们进入实战环节。一个可复现的环境是成功的第一步。
3.1 环境配置与核心库安装
我强烈建议使用conda或venv创建独立的Python环境,避免包冲突。以下是核心依赖库:
# 创建并激活环境 (以conda为例) conda create -n medical-xai python=3.9 conda activate medical-xai # 安装核心库 pip install numpy pandas scikit-learn matplotlib seaborn # 安装Jupyter Notebook(可选,用于交互式分析) pip install jupyter # 安装可解释AI核心库 pip install lime shapscikit-learn:用于构建和评估随机森林模型。lime:提供LIME解释功能。shap:提供SHAP解释功能,其内置的TreeExplainer对树模型有极快的计算速度。pandas,numpy:数据处理。matplotlib,seaborn:可视化。
3.2 模拟医疗数据集构建与理解
由于真实的患者数据涉及严格的隐私和安全规定,我们使用公开的scikit-learn内置的乳腺癌数据集进行演示。这是一个经典的二分类问题(恶性/良性),特征来自乳腺肿块的数字化图像,如半径、纹理、周长等,共30个特征,569个样本。这非常适合模拟一个肿瘤诊断场景。
import pandas as pd import numpy as np from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split # 加载数据 data = load_breast_cancer() X = pd.DataFrame(data.data, columns=data.feature_names) y = pd.Series(data.target) # 0: 恶性(Malignant), 1: 良性(Benign) # 查看数据基本信息 print(f"数据集形状: {X.shape}") print(f"特征示例:\n{X.iloc[:3, :5]}") # 查看前3行,前5列特征 print(f"\n目标变量分布:\n{y.value_counts()}") print(f"特征名称:\n{data.feature_names}") # 划分训练集和测试集 (8:2) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) print(f"\n训练集大小: {X_train.shape}, 测试集大小: {X_test.shape}")数据关键点解析:
- 特征工程:在实际医疗项目中,特征可能来自电子病历(EHR)、医学影像(需要CNN提取特征)、基因组学数据等。本项目的重点是可解释性方法,因此我们使用了已处理好的特征。但请记住,特征的质量和医学可解释性是可解释AI的前提。如果一个特征本身(如某个深度学习模型的中间层激活值)医生无法理解,那么解释它的贡献也意义有限。
- 类别不平衡:本数据集相对平衡。在实际中,如罕见病诊断,数据可能极度不平衡,需要采用过采样(如SMOTE)、欠采样或调整类别权重(如随机森林的
class_weight='balanced')等策略。 - 数据分割:我们使用
stratify=y进行分层抽样,确保训练集和测试集中恶性/良性的比例与原始数据集一致,这在小样本医疗数据中尤为重要。
4. 构建与评估随机森林诊断模型
有了数据,我们首先构建一个高性能的“黑盒”模型。
4.1 模型训练与基础评估
from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score # 初始化随机森林模型 # 设置random_state保证结果可复现,这在科学研究中必须做到 rf_model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42) # 训练模型 rf_model.fit(X_train, y_train) # 在测试集上进行预测 y_pred = rf_model.predict(X_test) y_pred_proba = rf_model.predict_proba(X_test)[:, 1] # 预测为恶性(类别1)的概率 # 模型性能评估 print("=== 随机森林模型性能评估 ===") print("\n1. 分类报告:") print(classification_report(y_test, y_pred, target_names=['恶性', '良性'])) print("\n2. 混淆矩阵:") print(confusion_matrix(y_test, y_pred)) print(f"\n3. ROC-AUC分数: {roc_auc_score(y_test, y_pred_proba):.4f}")参数选择心得:
n_estimators:树的数量。通常越多越好,但计算成本增加。100是一个稳健的起点,可以通过学习曲线观察性能是否饱和。max_depth:树的最大深度。这里限制为5,是为了防止过拟合,并且让生成的树更简单,便于我们后续理解。在实际应用中,可以通过交叉验证来调优。random_state:固定随机种子,确保每次运行结果一致,这对实验的复现性至关重要。
评估指标解读:
- 精确率、召回率、F1-score:对于医疗诊断,我们通常更关注召回率(即敏感性)。在癌症筛查中,我们宁愿多做一些假阳性(召回率高,但误诊了一些良性病例)的检查,也不愿漏掉一个真正的恶性病例(假阴性)。从分类报告中,我们可以重点观察“恶性”类别的召回率。
- ROC-AUC:它衡量模型在不同阈值下区分恶性与良性的整体能力。AUC越接近1越好,0.9以上通常被认为模型具有优秀的判别能力。
4.2 全局解释:随机森林特征重要性
在深入单个病例前,我们先从全局视角看看模型依赖了哪些特征。
import matplotlib.pyplot as plt import seaborn as sns # 获取特征重要性 feature_importance = pd.DataFrame({ 'feature': data.feature_names, 'importance': rf_model.feature_importances_ }).sort_values('importance', ascending=False) # 绘制特征重要性条形图 plt.figure(figsize=(10, 8)) sns.barplot(x='importance', y='feature', data=feature_importance.head(15)) # 展示前15个重要特征 plt.title('随机森林 - 全局特征重要性 (Top 15)') plt.xlabel('重要性分数') plt.tight_layout() plt.show() print("全局最重要的5个特征:") print(feature_importance.head())分析与临床对接: 生成的图表会显示如worst radius(最差半径)、worst perimeter(最差周长)等特征最为重要。这与医学常识是吻合的:恶性肿瘤细胞通常生长不受控制,导致肿瘤块体的半径、周长等形态学特征异常增大。
- 行动点:拿着这份特征重要性列表,与领域专家(肿瘤科医生)进行讨论。确认这些被模型认为重要的特征,是否确实是临床诊断中的关键指标。这一步是建立“模型可信度”的第一次握手。如果模型认为最重要的特征在医生看来无关紧要,那就要回头检查数据或特征工程是否有问题。
5. 局部解释实战:用LIME和SHAP打开“黑盒”
现在进入最核心的部分:对于测试集中的任意一个患者,我们要解释模型为什么做出这样的预测。我们选择一个模型预测为“恶性”,且预测概率很高的病例进行深入分析。
5.1 使用LIME进行局部解释
import lime import lime.lime_tabular # 1. 初始化LIME解释器 # 需要提供训练数据、特征名、类别名以及数据模式(‘classification‘) explainer_lime = lime.lime_tabular.LimeTabularExplainer( training_data=X_train.values, # LIME需要numpy数组 feature_names=data.feature_names, class_names=['恶性', '良性'], mode='classification', random_state=42 ) # 2. 从测试集中选择一个感兴趣的样本进行解释 (例如,第10个样本) sample_idx = 10 instance = X_test.iloc[sample_idx] true_label = y_test.iloc[sample_idx] pred_label = y_pred[sample_idx] pred_proba = y_pred_proba[sample_idx] print(f"样本索引: {sample_idx}") print(f"真实标签: {'恶性' if true_label == 0 else '良性'}") print(f"模型预测: {'恶性' if pred_label == 0 else '良性'} (概率: {pred_proba:.2%})") # 3. 生成解释 exp = explainer_lime.explain_instance( data_row=instance.values, predict_fn=rf_model.predict_proba, # 需要模型的概率预测函数 num_features=10 # 展示对当前预测影响最大的前10个特征 ) # 4. 可视化解释 # 显示在notebook中 exp.show_in_notebook() # 或者,将解释保存为HTML文件,便于分享和报告 exp.save_to_file('lime_explanation.html')LIME输出解读: LIME会生成一个横向条形图。对于“恶性”预测,图左侧(红色)的条形代表推动预测向“恶性”的特征,右侧(绿色)代表推动预测向“良性”的特征。条形的长度代表影响力大小。
- 例如,输出可能显示:
worst concave points > 0.15这个条件,将模型对“恶性”的预测概率提高了35%;而mean texture < 20这个条件,将“恶性”概率降低了10%。 - 临床价值:你可以这样向医生汇报:“对于这个病例,模型判断其为恶性的核心依据是‘最差凹点’这个指标异常高,超过了0.15的阈值,这与您关注的‘形态不规则’这个恶性征象是强相关的。同时,虽然其‘平均纹理’较低(良性倾向),但不足以抵消前者的影响。”
5.2 使用SHAP进行局部与全局解释
SHAP能提供更丰富、更理论坚实的解释视图。
import shap # 1. 初始化SHAP的树模型解释器(针对随机森林优化,速度极快) explainer_shap = shap.TreeExplainer(rf_model) # 2. 计算测试集所有样本的SHAP值(用于全局分析) shap_values = explainer_shap.shap_values(X_test) # 注意:对于二分类,shap_values是一个列表,shap_values[0]是类别0(恶性)的SHAP值,shap_values[1]是类别1(良性)的。 # 我们通常关注预测类别的SHAP值。这里我们查看“恶性”(类别0)的SHAP值。 shap_values_malignant = shap_values[0] # 3. 局部解释:对同一个样本进行解释 # 获取该样本的SHAP值 sample_shap_values = shap_values_malignant[sample_idx] # 创建力力图 print(f"\nSHAP力力图解释 (样本索引: {sample_idx})") shap.initjs() # 初始化JavaScript(在Notebook中需要) shap.force_plot( base_value=explainer_shap.expected_value[0], # 模型的基础输出值(所有样本的平均预测) shap_values=sample_shap_values, features=instance, feature_names=data.feature_names, matplotlib=True # 在非Notebook环境或需要保存时使用 ) # 在Jupyter Notebook中,更推荐使用 `shap.force_plot(...)` 而不加matplotlib=True,以获得交互式图表。 # 我们可以将力力图保存为HTML shap.save_html('shap_force_plot.html', shap.force_plot(explainer_shap.expected_value[0], sample_shap_values, instance, feature_names=data.feature_names))SHAP力力图解读:
- 基础值:模型对所有样本预测为“恶性”的平均概率。
- 箭头:每个特征将最终预测值从基础值“推”向某个方向。红色箭头(正向)代表该特征提高了恶性概率,蓝色箭头(负向)代表降低了恶性概率。箭头的长度代表影响力大小。
- 最终值:所有特征作用叠加后,模型对该样本的预测输出值(即
pred_proba)。 - 优势:力力图直观地展示了每个特征的贡献大小和方向,并且所有特征的贡献之和严格等于预测值与平均值的差,这满足了SHAP的“局部准确性”公理。
5.3 SHAP全局解释:揭示特征的整体影响模式
# 1. 特征重要性总结图(另一种视角) plt.figure() shap.summary_plot(shap_values_malignant, X_test, plot_type="bar", show=False) plt.title('SHAP特征重要性 (基于平均绝对SHAP值)') plt.tight_layout() plt.show() # 2. 特征依赖摘要图(最强大的全局解释工具) plt.figure() shap.summary_plot(shap_values_malignant, X_test, show=False) plt.title('SHAP特征依赖摘要图') plt.tight_layout() plt.show()SHAP摘要图解读:
- 条形图:基于SHAP值绝对值的均值对特征排序。这与随机森林自带的特征重要性大体一致,但基于更稳健的理论。
- 散点图:这是SHAP的精华。
- Y轴:特征,按重要性排序。
- X轴:该特征对应的SHAP值(对恶性预测的贡献度)。
- 颜色:表示特征值本身的大小(红色高,蓝色低)。
- 解读:以
worst radius为例,我们会看到,当特征值很大(红点)时,其SHAP值基本为正且很大,说明大的“最差半径”强烈预示着恶性。所有红点集中在右侧,蓝点集中在左侧,形成了一个清晰的梯度,这表明模型学到了一个单调且一致的关系——这非常符合临床直觉!
5.4 LIME与SHAP的对比与联合使用心得
在实际项目中,我习惯将两者结合使用:
- 快速验证与沟通用LIME:LIME的输出(特征+贡献度)非常像一句句逻辑陈述,易于转化成自然语言向临床专家解释。例如:“因为特征A高,所以风险+30%;因为特征B低,所以风险-10%。” 在会议或报告中,用LIME的结论开场,更容易被接受。
- 深入分析与发现模式用SHAP:当需要探究特征与预测结果之间的深层关系(如是否存在非线性、阈值效应、特征交互)时,SHAP的摘要图是无价之宝。它可以帮助我们发现数据中潜在的生物标志物相互作用。
- 一致性检查:对于一个病例,同时运行LIME和SHAP。如果两者指出的关键驱动特征大致相同,那么我们对解释的信心会大大增强。如果差异很大,就需要警惕:可能是LIME的扰动样本不够有代表性,或者模型在该局部区域的行为非常不稳定,这本身就是一个需要深入分析的风险信号。
实操心得:解释结果一定要回溯到原始数据。不要只看图表。对于SHAP或LIME指出的关键特征,去查看该患者这项特征的具体数值,并与正常范围、其他患者进行对比。确保模型的“理由”建立在真实、合理的数值差异上,而不是数据噪声或异常值上。
6. 构建可解释性诊断报告与系统集成思路
解释的最终目的是辅助决策。我们需要将上述分析打包成一份结构化的报告。
6.1 自动化生成单病例解释报告
我们可以编写一个函数,为任意输入样本自动生成包含模型预测、LIME和SHAP解释的摘要。
def generate_explanation_report(model, explainer_lime, explainer_shap, instance, feature_names, class_names): """ 为单个病例生成可解释性报告。 """ # 1. 模型预测 proba = model.predict_proba(instance.values.reshape(1, -1))[0] pred_class = model.predict(instance.values.reshape(1, -1))[0] report = f""" ===== AI辅助诊断可解释性报告 ===== 病例ID: (可根据实际情况填入) 模型预测: {class_names[pred_class]} 预测概率: [恶性: {proba[0]:.2%}, 良性: {proba[1]:.2%}] --- 局部解释 (LIME) --- 主要决策依据(前5位): """ # 获取LIME解释 exp_lime = explainer_lime.explain_instance(instance.values, model.predict_proba, num_features=5) for feature, weight in exp_lime.as_list(label=pred_class): influence = "增加风险" if weight > 0 else "降低风险" report += f" - 特征 `{feature}`: {influence} ({abs(weight):.2%})\n" report += f""" --- 局部解释 (SHAP) --- 特征贡献力力图摘要: (此处可描述SHAP力力图的主要发现,或嵌入力力图HTML链接) 关键正向驱动特征(提升恶性概率): """ # 计算SHAP值 shap_vals = explainer_shap.shap_values(instance.values.reshape(1, -1))[0][0] # 获取恶性类别的SHAP值 # 创建特征贡献字典并排序 contrib_dict = dict(zip(feature_names, shap_vals)) top_positive = sorted([(f, v) for f, v in contrib_dict.items() if v > 0], key=lambda x: x[1], reverse=True)[:3] top_negative = sorted([(f, v) for f, v in contrib_dict.items() if v < 0], key=lambda x: x[1])[:3] for feat, val in top_positive: report += f" - `{feat}`: +{val:.4f}\n" report += " 关键负向驱动特征(降低恶性概率):\n" for feat, val in top_negative: report += f" - `{feat}`: {val:.4f}\n" report += """ --- 全局上下文参考 --- (此处可加入该病例的特征值与全体患者分布的比较,例如百分位数) """ return report # 为之前选中的样本生成报告 report = generate_explanation_report(rf_model, explainer_lime, explainer_shap, instance, data.feature_names, ['恶性', '良性']) print(report)6.2 系统集成与部署考量
要将这套可解释性流程产品化,需要考虑以下几点:
- 性能:SHAP计算所有样本的解释可能较慢。在生产环境中,通常只对高不确定性预测(预测概率接近0.5)、与医生意见不一致的预测或随机抽检的病例进行详细解释。TreeSHAP对于随机森林已经非常快,但对于超大规模数据集仍需优化。
- 可视化集成:将SHAP力力图、摘要图或LIME的HTML报告集成到医疗诊断系统的用户界面(UI)中。医生在查看AI诊断建议时,可以一键点击“查看诊断依据”,弹出可视化图表。
- 解释的标准化:与临床专家共同定义“解释”的呈现格式。例如,是列出Top-3特征,还是需要给出类似于“符合/不符合典型恶性征象”的定性结论?将模型输出的数值贡献,翻译成医生熟悉的临床语言模板。
- 持续监控与反馈:建立机制,允许医生对AI的解释进行反馈(如“解释合理”、“解释不相关”)。这些反馈数据可用于持续评估和改进可解释性方法本身,甚至反哺模型优化。
7. 常见陷阱、挑战与应对策略
在实际应用中,我踩过不少坑,这里分享几个关键点:
- “垃圾进,垃圾出”:如果模型本身性能很差(AUC很低),那么解释它的错误决策意义不大。可解释性不能弥补糟糕的模型性能。务必先确保模型达到临床可接受的性能基准。
- 特征相关性误导:SHAP和LIME计算的是每个特征的边际贡献。如果两个特征高度相关(如“肿瘤半径”和“肿瘤面积”),它们的贡献可能会被分散或产生误导。解决方案包括:使用领域知识进行特征筛选、构建不相关的复合特征,或者在解释时明确指出这些特征在临床上是同一概念的不同度量。
- 解释的稳定性:LIME由于基于随机扰动,多次运行对同一个样本的解释可能有细微差异。SHAP则具有理论保证的稳定性。对于关键病例,可以多次运行LIME取平均,或主要依赖SHAP的解释。
- “为什么”不等于“应该”:可解释AI告诉我们模型是如何做决策的,而不是应该如何做决策。如果模型因为数据偏差学到了错误的关联(例如,将某个检测仪器的批次号作为预测特征),解释工具也会忠实地反映这一点。必须由人类专家对解释结果进行医学合理性审查。
- 计算资源:对深度学习模型使用SHAP的KernelExplainer或LIME可能会非常慢。对于图像或文本模型,需要用到专门的可解释性方法(如Grad-CAM, LIME for images),其复杂度和计算量更高。
8. 总结与展望:让AI成为医生的“透明伙伴”
通过这个项目,我们完成了一个从数据到模型,再到可解释输出的完整闭环。随机森林提供了坚实的预测基础,LIME和SHAP则像两盏聚光灯,从不同角度照亮了模型内部的决策逻辑。
我个人的体会是,可解释AI在医疗领域的价值,远不止于“让医生更信任AI”这么简单。它更是一个强大的模型调试与知识发现工具。多次通过SHAP图发现,模型强烈依赖某个我们之前未重视的实验室指标,这促使我们与医生展开新的科研讨论,有时甚至能发现潜在的、新的生物标志物。
未来,可解释性的趋势是交互式和因果性。静态的报告会演变成医生可以与AI对话的界面:“如果这个病人的某项指标降到正常范围,预测结果会如何变化?”(反事实解释)。更进一步,我们不仅想知道特征与结果的相关性,更想探究其因果关系,这将是下一代可解释AI在医疗中攻克的方向。
最后一个小技巧:在向临床专家汇报时,不要一上来就展示SHAP的散点图。先从一两个具体的、预测正确的疑难病例开始,用LIME生成一句句简单的“因为...所以...”的解释语句。当他们点头认可后,再引出SHAP来展示模型整体上稳健、符合临床规律的模式。这样由点到面,接受度会高得多。技术要让位于沟通,毕竟,我们的共同目标是更好地服务患者。