使用机器学习分类、提示工程、文本嵌入的特征工程和OpenAI API的模型可解释性的Python实现
这些天,大型语言模型和应用程序或工具在新闻和社交VoAGI上无处不在。GitHub的热门页面展示了广泛使用大型语言模型的存储库。我们已经看到了大型语言模型在市场营销写作、文档摘要、音乐创作和软件开发的代码生成方面的惊人能力。
企业内部和在线积累了大量的表格数据(一种最古老、最普遍的数据格式,可以用行和列组成的表格来表示)。我们能否在传统的机器学习生命周期中应用大型语言模型来改善模型性能并增加业务价值呢?
在本文中,我们将通过完整的Python实现代码探索以下主题:
- 在Kaggle的心脏病发作分析和预测数据集上构建广义线性模型和基于树的模型。
- 使用提示工程将表格数据转换为文本。
- 使用OpenAI API进行零样本分类(GPT-3.5模型:text-davinci-003)。
- 使用OpenAI嵌入API(text-embedding-ada-002)提高机器学习模型性能。
- 使用OpenAI API进行预测可解释性(gpt-3.5-turbo)。
数据集描述
该数据可在Kaggle网站上使用CC0 1.0通用许可证(CC0 1.0)公共领域奉献许可证获取,即没有版权(您可以复制、修改、分发和执行该作品,甚至可以用于商业目的)。请参考下面的链接:
心脏病发作分析和预测数据集
用于心脏病发作分类的数据集
www.kaggle.com
它包含人口统计特征、医疗状况和目标变量。以下是各列的说明:
- age:申请人年龄
- sex:申请人性别
- cp:胸痛类型:值1表示典型心绞痛,值2表示非典型心绞痛,值3表示非心绞痛性疼痛,值4表示无症状。
- trtbps:静息血压(以毫米汞柱为单位)
- chol:通过BMI传感器获取的胆固醇值(以毫克/分升为单位)
- fbs:空腹血糖 > 120毫克/分升,1 = True,0 = False
- restecg:静息心电图结果
- thalachh:达到的最大心率
- exng:运动诱发心绞痛(1 = 是;0 = 否)
- oldpeak:之前的峰值
- slp:斜率
- caa:主要血管的数量
- thall:thal评级
- output:目标变量,0=心脏病发作的可能性较小,1=心脏病发作的可能性较大
机器学习模型
开发了二分类模型来预测患有心脏病发作的可能性。本节将涵盖以下内容:
- 预处理:检查缺失值、独热编码、训练测试分层拆分等。
- 构建4个模型,包括三个广义线性模型和一个基于树的模型:逻辑回归、岭回归、Lasso和随机森林。
- 使用AUC进行模型评估。
首先,让我们导入包、加载数据、进行预处理和训练测试拆分。
import warningswarnings.filterwarnings("ignore")# 数学和向量import pandas as pdimport numpy as np# 可视化import plotly.express as px# 机器学习from sklearn.model_selection import train_test_splitfrom sklearn.metrics import roc_auc_scoreimport concurrent.futures# 实用函数from utils import prediction, compile_prompt, get_embedding, ml_models, create_auc_chart, gpt_reasoningpd.set_option('display.max_columns', None)# 加载数据df = pd.read_csv("./data/raw data/heart_attack_predicton_kaggle.csv")df.shape# 检查缺失值df.isna().sum()# 检查结果分布df['output'].value_counts()# 独热编码cat_cols = ['sex','exng','cp','fbs','restecg','slp','thall']df_model = pd.get_dummies(df,columns=cat_cols)df_model.shape# 训练测试分层拆分# 分离自变量和因变量X = df_model.drop(axis=1,columns=['output'])y = df_model['output'].tolist()X_tr, X_val, y_tr, y_val = train_test_split(X, y, test_size=0.2, random_state=101, stratify=y,shuffle=True)
现在,让我们构建模型对象,拟合模型,在测试集上进行预测并计算AUC。
## 模型函数def ml_models(): lr = LogisticRegression(penalty='none', solver='saga', random_state=42, n_jobs=-1) lasso = LogisticRegression(penalty='l1', solver='saga', random_state=42, n_jobs=-1) ridge = LogisticRegression(penalty='l2', solver='saga', random_state=42, n_jobs=-1) rf = RandomForestClassifier(n_estimators=300, max_depth=5, min_samples_leaf=50, max_features=0.3, random_state=42, n_jobs=-1) models = {'LR': lr, 'LASSO': lasso, 'RIDGE': ridge, 'RF': rf} return modelsmodels = ml_models()lr = models['LR']lasso = models['LASSO'] ridge = models['RIDGE'] rf = models['RF'] pred_dict = {}for k, m in models.items(): print(k) m.fit(X_tr, y_tr) preds = m.predict_proba(X_val)[:,1] auc = roc_auc_score(y_val, preds) pred_dict[k] = preds print(k + ': ', auc)
接下来,让我们可视化并比较模型的性能(AUC)。
在这个可视化中:
- 基于树的模型(随机森林)表现最好,具有更高的AUC。
- 三个广义线性模型的性能水平相似,AUC低于树模型,这是预期的。
使用OpenAI API进行零样本分类
我们将在基于文本-davinci-003模型的OpenAI API上对表格数据进行零样本分类。在深入了解Python实现之前,让我们更多地了解一下零样本分类。来自Hugging face的定义如下:
零样本分类是在训练期间模型未见过的类别进行预测的任务。这种方法利用了预训练的语言模型,可以被认为是迁移学习的一个实例,通常指的是在与其最初训练的不同应用中使用一个模型。这对于标记数据量较小的情况特别有用。
在零样本分类中,向模型提供了一个提示和一系列描述我们希望模型执行的操作的文本,而没有提供任何预期行为的示例。本节将涵盖以下内容:
- 用于提示工程的表格数据的预处理
- 提示语言模型
- 使用GPT-3.5 API进行零样本预测:文本-davinci-003
- 使用AUC进行模型评估
表格数据的预处理
首先,让我们在提示之前处理数据:
df_gpt = df.copy()df_gpt['sex'] = np.where(df_gpt['sex'] == 1, '男性', '女性')df_gpt['cp'] = np.where(df_gpt['cp'] == 1, '典型心绞痛', np.where(df_gpt['cp'] == 2, '非典型心绞痛', np.where(df_gpt['cp'] == 3, '非心绞痛痛', '无症状')))df_gpt['fbs'] = np.where(df_gpt['fbs'] == 1, '空腹血糖>120 mg/dl', '空腹血糖<=120 mg/dl')df_gpt['restecg'] = np.where(df_gpt['restecg'] == 0, '正常', np.where(df_gpt['restecg'] == 1, 'ST-T波异常(T波倒置和/或ST段抬高或压低>0.05 mV)', "Estes标准显示可能或确定的左室肥厚"))df_gpt['exng'] = np.where(df_gpt['exng'] == 1, '运动诱发心绞痛', '无运动诱发心绞痛')df_gpt['slp'] = np.where(df_gpt['slp'] == 0, '运动峰值ST段的斜率下降', np.where(df_gpt['slp'] == 1, '运动峰值ST段的斜率平坦', '运动峰值ST段的斜率上升'))df_gpt['thall'] = np.where(df_gpt['thall'] == 1, 'Thall为固定缺陷', np.where(df_gpt['thall'] == 2, 'Thall为正常', 'Thall为可逆缺陷'))# 将df转换为字典application_list = X_val.to_dict(orient='records')len(application_list)
提示 LLMs
提示是与大型语言模型进行特定任务交互的强大工具。提示是用户提供的输入,模型需要对其进行回应。提示可以是各种形式,例如文本、图像。
在本文中,提示包括具有预期的 JSON 输出格式和问题本身的说明。使用心脏病数据集,文本提示可以如下:
接下来,我们将定义提示和 API 调用函数,该函数构建提示并从 OpenAI-3.5 API 获取响应。
def prediction_GPT3_5(data, explain = False): if explain: prompt = prompt_logic(explain) else: prompt = prompt_logic(explain) print(prompt) response = openai.Completion.create( model = 'text-davinci-003', prompt=prompt, max_tokens=64, n=1, stop=None, temperature=0.5, top_p=1.0, frequency_penalty=0.0, presence_penalty=0.0 ) try: output = response.choices[0].text.strip() output_dict = json.loads(output) return output_dict except (IndexError, ValueError): return Nonedef prediction(combined_data_argu): application_data, explain = combined_data_argu response = prediction_GPT3_5(application_data, explain) return response
获取 API 响应 — 多进程
利用多进程加速 API 调用。代码如下:
### 从 GPT-3.5 模型(text-davinci-003)获取预测结果 - 多进程池with concurrent.futures.ThreadPoolExecutor() as executor: # 将 credit_data 和 explain 组合成单个可迭代对象 combined_data = zip(application_list, [False] * len(application_list)) # 将事务处理任务提交给执行器 results = executor.map(prediction, combined_data) # 将响应收集到列表中 responses = list(results)responses_df = pd.DataFrame(responses)responses_df.shape
零样本分类 AUC
零样本分类的 AUC 为 0.48,这表明预测结果比随机猜测更差,同时也表明 GPT-3.5 模型(text-davinci-003)在该数据集上可能没有泄露。
auc_gpt= roc_auc_score(y_val, responses_df['output'])auc_gpt
使用 OpenAI 嵌入提高机器学习模型性能
LLM 嵌入是大型语言模型(例如 OpenAI API)的一个端点,它可以轻松执行自然语言和代码任务,如语义搜索、聚类、主题建模和分类。通过提示工程,将表格数据转化为自然语言文本,从而生成嵌入。这些嵌入有可能通过使机器学习模型更好地理解自然语言并适应上下文,从而提高传统机器学习模型的性能,而只需使用少量标记数据。简而言之,它是在此上下文中的一种特征工程。
特征工程是将原始数据转换为更好地表示预测模型底层问题的特征,从而提高模型在未见数据上的准确性。
在本节中,您将了解到:
- 如何通过 API 调用获取 OpenAI 嵌入
- 模型性能比较 — 带有嵌入特征与不带嵌入特征
首先,让我们定义函数通过 API 获取嵌入,并将其与原始数据集合并:
# 定义获取嵌入的函数def get_embedding(text, model="text-embedding-ada-002"): text = text.replace("\n", " ") return openai.Embedding.create(input = [text], model=model)['data'][0]['embedding']# API 调用并与原始数据集合并df_gpt['ada_embedding'] = df_gpt.combined.apply(lambda x: get_embedding(x, model='text-embedding-ada-002'))df_gpt = df_gpt.join(pd.DataFrame(df_gpt['ada_embedding'].apply(pd.Series)))df_gpt.drop(['combined', 'ada_embedding'], axis = 1, inplace = True)df_gpt.columns = df_gpt.columns.tolist()[:14] + ['Embedding_' + str(i) for i in df_gpt.columns.tolist()[14:]]df = pd.concat([df, df_gpt[[i for i in df_gpt.columns.tolist() if i.startswith('Embedding_')]]], axis=1)df_gpt.shape
与纯机器学习模型类似,我们还将进行分层拆分并拟合模型:
# 分离自变量和因变量X = df.drop(axis=1,columns=['output'])y = df['output'].tolist()X_tr, X_val, y_tr, y_val = train_test_split(X, y, test_size=0.2, random_state=101, stratify=y,shuffle=True)models = ml_models()lr = models['LR']lasso = models['LASSO'] ridge = models['RIDGE'] rf = models['RF'] pred_dict_gpt = {}for k, m in models.items(): print(k) m.fit(X_tr, y_tr) preds = m.predict_proba(X_val)[:,1] auc = roc_auc_score(y_val, preds) pred_dict_gpt[k + '_With_GPT_Embedding'] = preds print(k + '_With_GPT_Embedding' + ': ', auc)
模型性能比较 — 带有嵌入特征与不带嵌入特征
通过将不带嵌入特征的模型组合起来,我们总共有8个模型。测试集上的ROC曲线如下:
pred_dict_combine = dict(list(pred_dict.items()) + list(pred_dict_gpt.items()))create_auc_chart(pred_dict_combine, y_val, 'Model AUC')
总体而言,我们观察到:
- 嵌入特征对广义线性模型(逻辑回归、Ridge和Lasso)的性能没有显著改善
- 带有嵌入特征的随机森林模型表现最好,略优于不带嵌入特征的随机森林模型
我们看到大型语言模型被整合到传统模型训练过程中可以提高输出的质量。我们可能会有一个问题,大型语言模型是否有助于解释模型的决策?让我们在下一节中探讨这个问题。
使用OpenAI API进行模型可解释性 — gpt-3.5-turbo
模型可解释性是机器学习应用中的一个关键主题,特别是在保险、医疗、金融和法律等领域,用户需要了解模型在局部和全局层面上的决策原理。如果您想了解更多关于深度学习模型解释的内容,我写了一篇关于使用SHAP进行深度学习模型解释的文章。
本节内容包括:
- 为OpenAI API准备输入
- 通过gpt-3.5-turbo模型进行推理
首先,让我们为API调用准备输入。
application_data = application_list[0]application_data{'age': 51, 'sex': 'Male', 'cp': 'Atypical angina', 'trtbps': 125, 'chol': 245, 'fbs': 'Fasting blood sugar > 120 mg/dl', 'restecg': 'Normal', 'thalachh': 166, 'exng': 'Without exercise induced angina', 'oldpeak': 2.4, 'slp': 'The slope of the peak exercise ST segment is flat', 'caa': 0, 'thall': 'Thall is normal'}
接下来,让我们通过调用gpt-3.5-turbo API来获取推理结果。
message_objects = [ {"role": "system", "content": '''You are a medical expert / underwriter in a global insurance company. Your job is to evaluate the chance of having heart attack. Please encode your response as json in the following format {{ "decision": "<Either less chance of heart attack or more chance of heart attack>", }}'''}, {"role": "user", "content": prompt}, ]completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=message_objects, max_tokens=1000, # 根据您所需的响应长度调整max_tokens stop=None, # 如果需要,设置自定义停止条件 )# 提取响应消息的内容 response_content = completion.choices[0].message["content"]
回应非常令人印象深刻,这个大型语言模型展示出强大的推理能力,并提供了合理的解释。
{ “决策”: “心脏病发作几率较低”, “推理”: “根据提供的信息,申请人有几个因素表明心脏病发作的几率较低。首先,申请人的年龄为51岁,不算年轻,但也不属于高风险心脏病发作的范围。其次,申请人为男性。虽然男性心脏病发作的风险通常比女性高,但这并不是唯一的决定因素。第三,申请人报告的胸痛类型为非典型心绞痛。非典型心绞痛的特征是胸痛不太可预测,可能与典型心绞痛有不同的模式。这可能表明与典型心绞痛相比,心脏病发作的风险较低。第四,申请人的静息血压为125毫米汞柱。这在正常范围内,不表示高血压,高血压是心脏病发作的一个危险因素。第五,申请人的胆固醇水平为245毫克/分升。虽然这超过了推荐水平,但并不算极高,而且申请人的BMI未提供,所以我们无法确定胆固醇水平是否由肥胖引起。第六,申请人的空腹血糖水平未超过120毫克/分升,这表明患糖尿病的风险较低,糖尿病是心脏病发作的另一个危险因素。第七,申请人的静息心电图结果正常,表明心脏功能正常,降低了心脏病发作的风险。第八,申请人的最大心率为166,这是一个好兆头,表明心血管系统更健康。第九,申请人没有运动诱发的心绞痛,这是另一个积极的因素。第十,相对于休息,运动引起的ST段压低为2.4,这在正常范围内,不表示重要缺血。第十一,运动峰值ST段的斜率是平坦的,这可能是正常的发现,也可能与申请人报告的非典型心绞痛有关。最后,申请人没有主要血管和正常的Thall,表明冠状动脉疾病的风险较低。考虑到所有这些因素,申请人发生心脏病发作的几率较低。然而,重要的是要注意,这个评估仅基于提供的信息,可能需要进一步的医学评估才能做出最终确定。” }
总结
大型语言模型是解决各行各业各种用例的强大工具。创建大型语言模型应用程序更加容易和经济实惠。大型语言模型肯定会为企业增加真正的商业价值。
在您离开之前
欢迎加入一个激动人心且富有成果的数据科学学习之旅!通过关注我的VoAGI页面保持联系,获取一系列引人入胜的数据科学内容。我将在未来几个月分享更多有关机器学习基础、自然语言处理基础和端到端数据科学实现的内容。干杯!
参考资料
- https://platform.openai.com/docs/guides/embeddings/what-are-embeddings
- https://platform.openai.com/docs/models/overview
什么是零样本分类?- Hugging Face
使用机器学习了解零样本分类
huggingface.co
大型语言模型简介:提示工程和P调节 | NVIDIA技术博客
ChatGPT产生了很大的影响。用户们都很兴奋地使用这个AI聊天机器人提问、写诗和灌输…
developer.nvidia.com
使用SHAP进行深度学习模型解释
图像和表格数据的Python实现
towardsdatascience.com