实用介绍 Transformer 模型:BERT
图片来源于 Alex Padurariu 在 Unsplash
动手教程
动手教程:如何使用 BERT 构建你的第一个情感分析模型
·
关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 7 月 17 日
–
前言:本文总结了关于给定主题的信息。它不应被视为原创研究。本文中包含的信息和代码可能受到我过去从各种在线文章、研究论文、书籍和开源代码中阅读或看到的内容的影响。
目录
-
BERT 简介
-
预训练和微调
-
实践操作:使用 BERT 进行情感分析
-
解释结果
-
结束语
在 NLP 中,变换器模型架构是一项革命性的进展,大大增强了理解和生成文本信息的能力。
在本教程中,我们将深入探讨 BERT,这是一种著名的基于变换器的模型,并提供一个实际的示例来微调基础 BERT 模型以进行情感分析。
BERT 介绍
BERT 由 Google 研究人员于 2018 年推出,是一种强大的语言模型,使用变换器架构。与早期的 LSTM 和 GRU 等单向或顺序双向模型架构相比,BERT 同时考虑过去和未来的上下文。这得益于创新的“注意力机制”,它使模型在生成表示时能够权衡句子中单词的重要性。
BERT 模型在以下两个 NLP 任务上进行了预训练:
-
掩码语言模型(MLM)
-
下一句预测(NSP)
并且通常被用作各种下游 NLP 任务的基础模型,例如我们在本教程中将涵盖的情感分析。
预训练和微调
BERT 的强大之处在于它的两步过程:
-
预训练是 BERT 在大量数据上进行训练的阶段。因此,它学会了预测句子中的掩码词(MLM 任务)和预测一个句子是否跟随另一个句子(NSP 任务)。这一阶段的输出是一个具有通用“语言理解”能力的预训练 NLP 模型。
-
微调是在特定任务上进一步训练预训练的 BERT 模型。模型以预训练的参数进行初始化,并在下游任务上对整个模型进行训练,使 BERT 能够根据当前任务的具体情况微调其语言理解能力。
实践操作:使用 BERT 进行情感分析
完整代码可以作为 GitHub 上的 Jupyter Notebook获得
在这个动手练习中,我们将使用 IMDB 电影评论数据集[4] (许可证:Apache 2.0)来训练情感分析模型,该数据集标记了评论是积极还是消极。我们还将使用 Hugging Face 的 transformers 库加载模型。
让我们加载所有库
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, roc_curve, auc
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer
# Variables to set the number of epochs and samples
num_epochs = 10
num_samples = 100 # set this to -1 to use all data
首先,我们需要加载数据集和模型分词器。
# Step 1: Load dataset and model tokenizer
dataset = load_dataset('imdb')
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
接下来,我们将创建一个图表,以查看积极和消极类别的分布。
# Data Exploration
train_df = pd.DataFrame(dataset["train"])
sns.countplot(x='label', data=train_df)
plt.title('Class distribution')
plt.show()
图 1. 训练数据集的类别分布
接下来,我们通过对文本进行分词来预处理我们的数据集。我们使用 BERT 的分词器,将文本转换为与 BERT 词汇表对应的标记。
# Step 2: Preprocess the dataset
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
之后,我们准备训练和评估数据集。请记住,如果你想使用所有数据,可以将num_samples
变量设置为-1
。
if num_samples == -1:
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42)
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42)
else:
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(num_samples))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(num_samples))
接下来,我们加载预训练的 BERT 模型。我们将使用AutoModelForSequenceClassification
类,这是一种针对分类任务设计的 BERT 模型。
在本教程中,我们使用‘bert-base-uncased’版本的 BERT,它是基于小写英文文本进行训练的。
# Step 3: Load pre-trained model
model = AutoModelForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)
现在,我们准备定义我们的训练参数并创建一个Trainer
实例来训练我们的模型。
# Step 4: Define training arguments
training_args = TrainingArguments("test_trainer", evaluation_strategy="epoch", no_cuda=True, num_train_epochs=num_epochs)
# Step 5: Create Trainer instance and train
trainer = Trainer(
model=model, args=training_args, train_dataset=small_train_dataset, eval_dataset=small_eval_dataset
)
trainer.train()
结果解释
训练完模型后,让我们来评估一下。我们将计算混淆矩阵和 ROC 曲线,以了解我们的模型表现如何。
# Step 6: Evaluation
predictions = trainer.predict(small_eval_dataset)
# Confusion matrix
cm = confusion_matrix(small_eval_dataset['label'], predictions.predictions.argmax(-1))
sns.heatmap(cm, annot=True, fmt='d')
plt.title('Confusion Matrix')
plt.show()
# ROC Curve
fpr, tpr, _ = roc_curve(small_eval_dataset['label'], predictions.predictions[:, 1])
roc_auc = auc(fpr, tpr)
plt.figure(figsize=(1.618 * 5, 5))
plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc)
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()
图 2. 混淆矩阵
图 3. ROC 曲线
混淆矩阵详细地分解了我们的预测与实际标签的匹配情况,而 ROC 曲线则展示了在不同阈值设置下,真正例率(敏感度)与假正例率(1 — 特异度)之间的权衡。
最后,为了查看我们的模型的实际效果,让我们用它来推断样本文本的情感。
# Step 7: Inference on a new sample
sample_text = "This is a fantastic movie. I really enjoyed it."
sample_inputs = tokenizer(sample_text, padding="max_length", truncation=True, max_length=512, return_tensors="pt")
# Move inputs to device (if GPU available)
sample_inputs.to(training_args.device)
# Make prediction
predictions = model(**sample_inputs)
predicted_class = predictions.logits.argmax(-1).item()
if predicted_class == 1:
print("Positive sentiment")
else:
print("Negative sentiment")
结语
通过对 IMDb 电影评论的情感分析示例进行讲解,我希望你能清晰地理解如何将 BERT 应用于现实世界的自然语言处理问题。我在这里包含的 Python 代码可以进行调整和扩展,以应对不同的任务和数据集,为更复杂和准确的语言模型铺平道路。
参考文献
[1] Devlin, J., Chang, M. W., Lee, K., & Toutanova, K. (2018). BERT:用于语言理解的深度双向变换器的预训练。arXiv 预印本 arXiv:1810.04805
[2] Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., … & Polosukhin, I. (2017). 注意力机制即一切。在《神经信息处理系统进展》中(第 5998–6008 页)。
[3] Wolf, T., Debut, L., Sanh, V., Chaumond, J., Delangue, C., Moi, A., … & Rush, A. M. (2019). Huggingface 的变换器:最先进的自然语言处理。ArXiv, abs/1910.03771。
[4] Lhoest, Q., Villanova del Moral, A., Jernite, Y., Thakur, A., von Platen, P., Patil, S., Chaumond, J., Drame, M., Plu, J., Tunstall, L., Davison, J., Šaško, M., Chhablani, G., Malik, B., Brandeis, S., Le Scao, T., Sanh, V., Xu, C., Patry, N., McMillan-Major, A., Schmid, P., Gugger, S., Delangue, C., Matussière, T., Debut, L., Bekman, S., Cistac, P., Goehringer, T., Mustar, V., Lagunas, F., Rush, A., & Wolf, T. (2021). 数据集:自然语言处理的社区库。收录于 2021 年自然语言处理实证方法会议:系统演示 (第 175–184 页)。在线和多米尼加共和国蓬塔卡纳:计算语言学协会。取自 aclanthology.org/2021.emnlp-demo.21
感谢阅读。如果您有任何反馈,请随时通过评论此帖子、在 LinkedIn上给我留言,或发送电子邮件 (smhkapadia[at]gmail.com)
如果您喜欢这篇文章,请访问我的其他文章
逐步指南:为任何领域微调预训练的 NLP 模型
towardsdatascience.com [## 自然语言处理的演变
语言模型发展的历史视角
medium.com](https://medium.com/aimonks/the-evolution-of-natural-language-processing-56ce27916e10?source=post_page-----4715ed0deede--------------------------------) ## Python 中的推荐系统:LightFM
使用 LightFM 在 Python 中构建推荐系统的逐步指南
towardsdatascience.com ## 评估主题模型:潜在狄利克雷分配 (LDA)
构建可解释的主题模型的逐步指南
towardsdatascience.com
使用 Azure ML 实现实用的 MLOps
图片由 Luca Bravo 提供,来源于 Unsplash
使用 Azure ML CLI(v2) 和 github actions 自动化 ML 流程
·
关注 发表在 Towards Data Science · 10 分钟阅读 · 2023 年 2 月 20 日
–
介绍
机器学习模型像我们日常使用的软件产品一样,影响着我们与世界的互动。正如 DevOps 对于无缝的 CI/CD 是必要的,MLOps 也变得至关重要,以不断构建最新的模型并利用其预测结果。
在本文中,我们将使用 Azure ML CLI(v2) 和 Github Actions 构建端到端的 MLOps。本文希望能成为你下一个 MLOps 项目的起点!
本文将帮助你模拟以下场景:
数据经常发生漂移并通过 API 提供。因此需要在规定的频率下重新训练模型并重新部署到在线端点。
通过以下步骤实现每周的定时任务:
-
使用 API 下载数据并将其注册为 Azure 数据集。
-
计算管理及触发 Azure ML Studio 中的模型训练任务。
-
注册在最新任务中创建的模型。
-
将新模型部署到在线端点。
在 Azure ML 中自动化 ML 流水线有两个选项:
1. Azure DevOps
2. Github Actions
你可以在这里阅读相关内容。我选择了 GitHub Actions 因为它的使用方便。
关于 MLOps 的快速回顾
MLOps 的需求及实现步骤:
-
通过一致性和变更跟踪进行质量控制:
a. 整个项目的初始设置 — IDE、工作区、权限
b. 环境版本控制
c. 数据版本控制
d. 代码版本控制
e. 其他组件的版本控制
-
模型的快速实验:
a. 跟踪模型超参数
b. 跟踪不同数据切片上的模型指标、偏差、公平性和可解释性
c. 维护 ML 流水线中变更部分之间的链接
-
无缝的模型部署和全面的模型监控:
a. 快速将模型部署到生产环境
b. 分阶段推出、蓝绿或其他部署策略
c. 跟踪模型效果以触发重新训练
d. 跟踪数据漂移以触发重新训练
范围
我将整个 ML 项目结构化为三个不同的流水线,以实现上述 MLOps 目标:
-
构建流水线
-
训练流水线
-
部署流水线
在讨论这些流水线后,我们将深入探讨代码实现。
1. 构建流水线
1.1 初始设置
步骤 1:设置 Azure ML Studio:
我们将使用 MS Azure 进行数据版本控制、模型训练和部署。请按照以下步骤创建你的 Azure ML 工作区:
-
登录到你的 Azure 帐户,或从这里获取免费的 Azure 订阅。
-
创建资源组(参考)。
-
创建 ML 工作区 - 访问 ml.azure.com,点击 创建工作区 并按照屏幕上的指示操作。
当你的 Azure ML 工作区创建完成后,你应该能看到如下界面:
通过 ml.azure.com 访问 Azure ML 工作区
你暂时无法看到这些任务,不用担心!
步骤 2:将 GitHub Actions 与 Azure ML Studio 链接:
在 GitHub 中初始化一个仓库并转到设置 > 秘密和变量 > Actions > 新建仓库秘密
打开一个新的浏览器标签页以创建一个服务主体以访问你的 ML 工作区。(ref)
将生成的 JSON 保存为 AZURE_CREDENTIALS 并存储在你的仓库秘密中。
仓库中的 GitHub Actions 密钥
第 3 步:生成个人访问令牌 (PAT)
使用普通的 GITHUB_TOKEN,你无法编辑工作流文件(.github/workflows 中的文件),你需要添加 PAT。你需要编辑工作流文件以自动化版本控制训练运行、模型版本等。
在 GitHub 中,从设置 > 开发者设置 > 个人访问令牌 生成 PAT。
用于编辑工作流的个人访问令牌
将 PAT 保存到你的仓库秘密中,并设置以下仓库权限:
PAT 的仓库权限
1.2 环境版本控制
需要两个环境:
-
训练环境 — 用于模型训练依赖
-
部署环境 — 用于模型服务依赖
对于较小的项目,你也可以将它们保存在相同的环境中。
1.3 数据版本控制
在我们的例子中,数据每周下载一次,并在 Azure 数据存储中注册为数据集。数据版本控制对于跟踪模型的血统非常重要。在我们的例子中,这是通过jobs/data_download.save_to_data_upload(…)
1.4 代码版本控制
代码版本控制通过 GitHub 完成。
1.5 其他组件的版本控制
你还需要版本控制 runs, models 和其他组件。为了自动化它们的命名,你需要在之前的 cron 作业中编辑它们。在我们的例子中,这是通过 jobs/update_training_yamls.py 完成的。
1.6 自动化测试
为了自动测试 Python 文件,我们可以使用 pytest 并跟踪代码覆盖率。
2. 训练管道
2.1 模型超参数跟踪
在我们的例子中,我们使用了 mlflow for pytorch 来记录以下详细信息:
指标:
每次训练任务中捕获的指标
运行时模型参数:
每次运行时版本控制的参数
2.2 在不同数据切片上跟踪模型指标、偏差、公平性和解释性
由于我使用了股票数据来模拟不断变化的数据,我跳过了这部分。然而,在大多数机器学习用例中,这些是判断模型性能的重要指标。
2.3 维护机器学习管道中变化部分之间的链接
Azure ML studio 自动链接所有内容。
训练模型时的示例血统跟踪:
训练任务快照
同样,数据和模型也会自动版本化并跟踪它们的历史。
3. 部署流水线
3.1 快速模型部署到生产环境
最初创建端点并部署第一个模型需要一些时间。之后,新注册的模型可以在更短的时间内用于更新端点。
3.2 分阶段推出、蓝绿部署或不同的部署策略
可以使用不同的部署策略。在我们的案例中,我们覆盖了之前部署的模型。然而,通过 Azure ML,蓝绿部署在部署过程中可以非常轻松地实现。
3.3 追踪模型效果以触发重新训练
模型部署后,我们需要跟踪模型对已知标签的性能。这将帮助我们识别模型在哪些数据层次上表现不佳。这将帮助我们确定是否需要收集更多数据或在下一次模型重新训练期间采取其他措施。
3.4 追踪数据漂移以触发重新训练
对于模型重新训练,有两种思路:
1. 基于计划
2. 基于漂移
两种选项各有优缺点。在这个例子中,我遵循基于计划的模型重新训练。因此,我没有实现任何数据漂移监控。然而,Azure 提供了监控数据漂移的工具。
代码
作为工程师,从头开始编写代码时会变得更加清晰。所以,尽情使用吧!
代码库: coderkol95/MLOps_stock_prediction
项目文件夹结构:
文件夹和文件的简要信息:
.github/workflows/
控制流水线的 yml 文件放在这里。这些文件通过 cron 作业触发数据下载/上传、模型训练、注册和部署。
data_pipeline.yml:频率 - 每周一凌晨 1:01
- 下载股票数据并更新 yml 文件
通过 API 下载数据到 csv 文件,并更新 data-upload.yml 文件,添加数据集标签、版本和路径。
- 编辑 yaml 文件
更新 yml 文件中其他组件的版本,如 job_name、模型版本等,这些将在运行期间使用。
- 将文件推送到 github
将更新的 yml 文件和下载的 csv 文件推送到代码库
- 上传到 azure
在 Azure 数据存储中注册数据集
name: data upload to azure
env:
ticker: WIPRO.NS
start: 366
end: 1
on:
schedule:
- cron: "1 1 * * 1"
jobs:
datawork:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@v2
with:
token: ${{ secrets.PAT }}
repository: 'coderkol95/MLOps_stock_prediction'
- name: setup python 3.9
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: install python packages
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: download ticker data and update yml file
run: python data_download.py --ticker $ticker --start $start --end $end
id: data
working-directory: jobs
- name: edit yaml files
run: python update_training_yamls.py
working-directory: jobs
- name: push files to github
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git commit -m "Ticker data for $ticker downloaded and YAML file updated." || exit 0
git push @github.com/${GITHUB_REPOSITORY}.git">https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git HEAD
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: azure login
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: setup
run: bash setup.sh
working-directory: cli
continue-on-error: true
- name: upload to azure
run: az ml data create -f jobs/data_upload.yml
model_pipeline.yml:频率 - 每周一凌晨 2:01
- 训练作业
在最新的数据集上计算创建和模型训练。
数据集准备和模型训练通过 pytorch lightning 完成。所有日志记录通过 MLFlow 进行。有关代码的详细信息,请参阅我的 代码库。
- 注册作业
从最新运行中注册模型。
- 删除计算
训练完成后计算删除。
name: training and registering model
env:
job_name: ga-run-10
compute_name: computer456
registered_model_name: GA_model
on:
schedule:
- cron: "1 2 * * 1"
jobs:
train-job:
runs-on: ubuntu-latest
steps:
- name: check out repo
uses: actions/checkout@v2
- name: azure login
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: setup
run: bash setup.sh
working-directory: cli
continue-on-error: true
- name: create-compute
run: az ml compute create --name $compute_name --size STANDARD_DS11_v2 --min-instances 1 --max-instances 2 --type AmlCompute
- name: train-job
working-directory: jobs
run: az ml job create --file train.yml --debug --stream # --stream causes the step to go on, as long as the model trains.
# If training is expected to take a long time, registration can be scheduled in a separate cron job, triggered later.
register-job:
needs: [train-job]
runs-on: ubuntu-latest
steps:
- name: check out repo
uses: actions/checkout@v2
- name: azure login
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: setup
run: bash setup.sh
working-directory: cli
continue-on-error: true
- name: register-model
run: az ml model create
--name $registered_model_name
--version 10
--path azureml://jobs/ga-run-10/outputs/artifacts/paths/outputs/
--type custom_model
delete-compute:
needs: [train-job]
runs-on: ubuntu-latest
steps:
- name: check out repo
uses: actions/checkout@v2
- name: azure login
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: setup
run: bash setup.sh
working-directory: cli
continue-on-error: true
- name: delete-step
run: az ml compute delete --name $compute_name --yes
deployment_pipeline.yml:频率 - 每周一凌晨 3:01
-
端点和部署创建(如果是第一次)
-
使用最新模型更新在线部署(如下所示)
name: model deployment
on:
schedule:
- cron: "1 3 * * 1"
jobs:
# compare-job:
# Compare if the model is good enough
# Profile the model
# If it is good enough, proceed to next step
deployment-job:
runs-on: ubuntu-latest
steps:
- name: check out repo
uses: actions/checkout@v2
- name: azure login
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: setup
run: bash setup.sh
working-directory: cli
continue-on-error: true
# Commenting out as endpoint creation is only needed during the first run
# - name: create-endpoint
# run: az ml online-endpoint create --name ga-deployment
- name: deployment-step
run: az ml online-deployment update -f deploy.yml #--all-traffic # First time it'll be az ml ... create --all-traffic
working-directory: jobs
我将 cron 作业的执行时间间隔设置为 1 小时,因为它在 1 小时内完成,你可以根据需要设置更长的间隔。你也可以设置标志来捕捉作业完成状态。
cli/
setup.sh: 配置运行代码的虚拟机,适用于 Azure ML。
GROUP="RG"
LOCATION="eastus"
WORKSPACE="AzureMLWorkspace"
az configure --defaults group=$GROUP workspace=$WORKSPACE location=$LOCATION
az extension remove -n ml
az extension add -n ml
jobs/
这里保存了特定于 Azure 的 YAML 文件以及用于单个管道步骤执行的 Python 脚本。
data_download.py get_ticker_data(…)
通过 API 调用下载数据并保存到 CSV 文件。我使用 Yahoo Finance API 下载了数据。
get_dataset_tags(…)
版本控制数据并添加标签。
在上传到 Azure 之前生成的数据集标签。
save_to_data_upload(…)
将数据集规格写入 Azure yml 文件,以便上传到 Azure 数据存储。
对于代码,你可以参考我的代码库。
data_upload.yml
这个 yml 文件由jobs/data_download.save_to_data_upload(…) 更新。这个 yml 文件将数据集上传到 Azure。
$schema: https://azuremlschemas.azureedge.net/latest/data.schema.json
type: uri_file
name: 'WIPRO'
description: Stock data for WIPRO.NS during 2022-02-14:2023-02-13 in 1d interval.
path: '../data/WIPRO.NS.csv'
tags: {'Length': 249, 'Start': '2022-02-14', 'End': '2023-02-13', 'Median': 413.7, 'SD': 69.09}
version: 20230215
deploy.yml
这个 yml 文件在每次 cron 作业前由jobs/update_training_yamls.py 更新。
$schema: https://azuremlschemas.azureedge.net/latest/managedOnlineDeployment.schema.json
name: green
endpoint_name: ga-deployment
model: azureml:GA_model:10
code_configuration:
code: ../jobs
scoring_script: deployment.py
environment: azureml:stock-pricing:5
instance_type: Standard_DS1_v2
instance_count: 1
deployment.py
在端点使用的脚本来生成在线预测。有关代码,你可以参考我的代码库。
init()
初始化模型和 PyTorch Lightning 使用的数据模块。
run()
用于提供来自模型的在线预测。
train.py
使用 PyTorch Lightning 和 MLflow 的模型训练脚本。有关代码,你可以参考我的代码库。
train.yml 这个 yml 文件在每次 cron 作业前由jobs/update_training_yamls.py 更新。
$schema: https://azuremlschemas.azureedge.net/latest/commandJob.schema.json
name: ga-run-10
tags:
modeltype: pytorch
code: ../jobs
command: >-
python train.py
--data ${{inputs.data}}
inputs:
data:
type: uri_file
path: azureml:WIPRO@latest
environment: azureml:stock-pricing:4
compute: azureml:computer456
display_name: stock
experiment_name: ga_train_job
description: Training job via Github actions
update_training_yamls.py
更新在每次运行前需要更新的多个组件的版本,如运行 ID、待注册的模型版本、待部署的模型。有关代码,你可以参考我的代码库。
结论
我希望你现在对如何使用 MS Azure 实现自动化端到端的 MLOps 项目有了了解。有关详细的代码实现,你可以参考我的代码库。
实用的提示工程
原文:
towardsdatascience.com/practical-prompt-engineering-74e96130abc4
成功提示 LLMs 的技巧和窍门…
·发表于Towards Data Science ·15 分钟阅读·2023 年 7 月 30 日
–
(照片由Jan Kahánek提供,来源于Unsplash)
由于其文本到文本的格式,大型语言模型(LLMs)能够用一个模型解决各种任务。这种能力最初通过像GPT-2和GPT-3这样的模型的零样本和少样本学习得到了展示[5, 6]。然而,当经过微调以符合人类的偏好和指示时,LLMs 变得更具吸引力,推动了流行的生成应用,如编码助手、信息寻求对话代理和基于聊天的搜索体验。
由于其能够实现的应用,LLMs 在研究界和大众文化中迅速崛起。在这一过程中,我们还见证了一个新的、互补的领域的出现:提示工程。从高层次看,LLMs 通过 i) 以文本(即提示)作为输入和 ii) 生成文本输出,从中提取有用信息(例如,分类、摘要、翻译等)。这种方法的灵活性非常有利。然而,我们必须确定如何正确构造输入提示,以便 LLM 能够生成所需的输出。
提示工程是一门实证科学,研究不同的提示策略如何优化 LLM 性能。尽管存在各种方法,我们将通过本概述构建对提示的一般机制的理解,以及一些基本的(但极其有效的!)提示技术,如零样本/少样本学习和指令提示。在此过程中,我们将学习实用技巧,并获得可立即采纳的要点,以成为更有效的提示工程师和 LLM 从业者。
(作者创建)
理解大型语言模型(LLMs)。 由于本文重点讨论提示技术,这里不会解释语言模型的历史或机制。为了更好地理解语言模型(这是深入理解提示的一个重要前提),我写了一些概述,供大家参考。以下是这些概述的列表(按重要性排序):
提示一览
语言模型可以使用其通用的文本到文本格式解决各种任务(来自 [1])
鉴于当前对 LLM 的热度,我们可能会问自己:LLM 的基本优势是什么,使它们如此强大? 尽管这个问题没有单一的答案(例如,模型规模,大规模预训练数据,人类反馈 等),LLM 的一个主要优势是其通用的文本到文本格式。这些模型擅长 下一个词预测,因此通过正确调整和利用这一技能可以解决许多不同的任务!
要解决一个任务,我们需要做的就是 i) 向模型提供包含相关信息的文本输入,并 ii) 从模型返回的文本中提取输出。这样统一的方法可以用于翻译、总结、问答、分类等。然而,事情并不(完全)那么简单。即,提供给 LLM 的提示(即输入文本)的措辞和结构可以显著影响模型的准确性。换句话说,提示工程非常重要。
什么是提示工程?
“提示工程是一个相对较新的学科,旨在开发和优化提示,以高效使用 LMs 处理各种应用和研究主题。” — 来自 [2]
由于正确制作提示内容对与 LLM 获得有用结果至关重要,提示工程在最近几个月引起了很多关注。然而,这是一门经验科学——发现最佳提示通常是基于启发式的,需要实验。我们可以通过 跟踪和版本管理我们的提示,测试不同的想法,来发现更好的提示。
用指令来提示 LLM(由作者创建)
提示的组成部分。 提示可以创建的选项有很多种。然而,大多数提示由相同的几个(可选)组件组成:
-
输入数据:这是 LLM 预计处理的实际数据(例如,要翻译或分类的句子,要总结的文档等)。
-
示例:展示 LLM 正确行为的最佳方法之一是提供几个具体的输入-输出对作为提示。
-
指令:我们可以通过指令在提示中用文字描述要做的事情,而不是展示具体的正确行为示例;见上文。
-
指标:以固定且可预测的结构向 LLM 提供输入是有帮助的,因此我们可以通过使用指标来分隔提示的不同部分;见下文。
-
上下文:除了上述描述的组件,我们可能还希望以某种方式向 LLM 提供额外的“上下文”或信息。
指标可以用来以多种方式结构化提示(由作者创建)
一般提示。 提示工程的细节根据所使用的模型和我们试图解决的任务差异很大。然而,有一些普遍接受的提示工程原则是有帮助的,要牢记这些原则 [1, 3]。
-
从简单开始:从简单的提示开始,然后逐步修改提示,同时跟踪经验结果。
-
直接:如果我们希望 LLM 匹配特定的风格或格式,我们应当明确而直接地说明。准确地说出你想要什么可以传达信息。
-
具体性:模糊性是每个提示工程师的敌人。我们应当使提示详细且具体,但不要过度提供过长的输入(即,提示的长度有 限制!)。
-
示例是强大的:如果描述我们想要的内容很困难,提供几个不同输入的正确输出或行为的具体示例可能会很有用。
语言模型的上下文窗口可视化(由作者创建)
上下文窗口。 当我们考虑不同的提示技巧和方法时,我们需要记住我们只能在提示中包含有限的数量的信息。所有 LLM 都有一个预定义的上下文窗口,设定了可以一次处理的总令牌数(即文本序列中的单词或子词)的限制。上下文窗口的大小在模型之间有所不同,但目前有强烈的推动力来增加上下文窗口的大小。例如,GPT-4 的上下文窗口为 32K 令牌,比 OpenAI 之前的任何模型都要 大 4 倍。
常见的提示技术
零样本和少样本学习的出现(来自 [4, 5, 6])
尽管 LLM 由于像ChatGPT这样的热门模型最近经历了爆炸性增长,但提示技术已经存在了相当一段时间。最初,像GPT [4]这样的模型经过微调以解决下游任务。随着GPT-2 [5]的提出,我们看到研究人员开始使用零样本学习来用单一的基础模型解决多个下游任务。最后,GPT-3 向我们展示了随着模型规模的增大,语言模型在少样本学习方面变得非常出色。在本节中,我们将深入探讨这些思想,以更好地了解零样本和少样本学习的工作原理,并提供一些更复杂的提示技术的细节。
零样本学习
(来自 [6])
零样本学习的基本思想相当简单。我们只需将任务描述和相关的输入数据提供给 LLM,并让它生成结果;见上文。由于观察到的大量预训练数据,LLM 通常非常有能力以这种方式解决任务。也就是说,它们可以利用其知识库来解决(相对)大量的任务;请见下文示例(由GPT-3.5生成)。
零样本学习与 GPT-3.5(作者创建)
零样本学习被像 GPT-2 这样的模型广泛探索,并在某些情况下表现良好。然而,如果零样本学习无法解决我们的任务,我们应该怎么做? 在许多情况下,我们可以通过提供更具体和明确的信息来大幅提高 LLM 的性能。特别是,我们可以开始在提示中添加期望输出的示例,让模型能够复制从提示中看到的数据模式。
少样本学习
除了任务描述之外,我们还可以通过高质量的输入输出示例来增强我们的提示。这种技术形成了少样本学习的基础,少样本学习试图通过提供明确的正确行为示例来提高大型语言模型(LLM)的性能。如果使用得当并应用于正确的模型,少样本学习非常有效,这一点通过如GPT-3 [6]等 LLM 的突破性能力得到了证明;请见下文。
(来自 [3])
然而,学习如何正确利用大型语言模型的少量示例学习能力可能很复杂。我们应该在提示中包含哪些示例?是否有正确的提示结构方式?提示的变化是否会显著影响大型语言模型?
大多数大型语言模型对提示的构造方式很敏感,这使得提示工程既困难又重要。尽管像 GPT-4 这样的最新模型似乎对提示中的小扰动不那么敏感 [2],但研究社区 [7] 给我们提供了一些有关如何正确使用少量示例学习的提示,这些提示仍然有助于理解:
-
示例的排序很重要,打乱少量示例的顺序可能会显著改变大型语言模型的表现。增加更多少量示例并不能解决这个问题。
-
少量示例中的标签分布很重要,应与实际的数据分布相匹配。令人惊讶的是,标签的正确性并不是那么重要。
-
大型语言模型倾向于重复最后一个少量示例(即近期效应)。
-
包含在提示中的示例应该是多样化且随机排序的。
最佳数据采样。 选择多样化、随机排序且与测试示例相关的示例是最好的。除此之外,还进行了大量研究,以确定如何为提示选择最佳示例。例如,少量学习样本可以通过多样性选择 [8]、基于不确定性的选择 [9],甚至是根据与测试示例的相似性进行选择 [10]。
(来源 [3])
少量学习 vs. 微调。 在继续之前,我想解决一个显著的 混淆点。少量学习不是微调。 少量学习是在提示中向大型语言模型提供示例,这些示例可以作为生成正确输出的相关上下文。这个过程被称为“上下文学习”;见上文。模型的参数不会通过少量学习进行修改。相比之下,微调明确地训练模型(即通过反向传播更新其权重)在选定的数据集上。
指令提示
使用调整过指令的语言模型作为编码助手(来源 [15])
少量学习非常强大,但它有一个显著的缺点:示例消耗大量的标记。由于大型语言模型的上下文窗口是有限的,我们可能需要探索不会消耗太多标记的提示方法。例如,我们能否通过文字解释正确的行为给大型语言模型? 简短的回答是可以!这种技术,即将书面指令作为提示的一部分,包括在内,被称为指令提示,它在特定类型的大型语言模型上表现最佳。
指令调优与对齐。 最近,语言模型的发展主要集中在提高指令跟随能力上。预训练的 LLM 在开箱即用时并不擅长遵循指令。然而,教会这些模型如何遵循指令能使它们更好地完成用户的需求(即,改善人类对齐)。遵循指令的 LLM 支持多种有用的应用,从信息检索对话代理(例如,ChatGPT)到编码助手(例如,Codex[13]);见下文。
(见[13, 14])
正如在之前的帖子中广泛讨论的那样,创建 LLM 的第一步是使用语言建模目标在大规模的未标记文本语料库上进行预训练。在此过程中,模型获取信息并学会准确地进行下一个词预测。然而,模型的输出并不总是有趣、引人入胜或有帮助,并且模型通常难以遵循复杂的指令。为了鼓励这种行为,我们需要超越基本的预训练。
创建遵循指令的 LLM。 有几种不同的方法可以教会 LLM 如何遵循指令。例如,我们可以进行指令调优[12],或者在包含指令的对话示例上微调 LLM。一些显著的模型采用了这种方法,比如LLaMA(及其变体)[15]、所有 FLAN 模型[12]、OPT-IML[16]等。或者,我们可以使用由监督微调(SFT)和来自人类反馈的强化学习(RLHF)组成的三步法;见下文。这种方法已经创造出了令人惊叹的模型,如 ChatGPT、GPT-4、Sparrow[17]等。
基于人类反馈对 LLM 进行对齐(见[13])
制定有用的指令。 如果我们能够访问到一个已经训练来遵循指令的 LLM,我们可以通过给模型提供有用和信息丰富的指令来完成很多工作。以下是一些使用指令提示的关键技巧和想法:
-
就像我们提示的其余部分一样,指令应该是具体和详细的。
-
我们应避免在提示中告诉 LLM 不要做某事。相反,我们应该专注于告诉 LLM 要做什么。
-
使用具有清晰指示的输入结构来识别提示中的指令是非常有帮助的;见下文。
指令提示的不同格式(由作者创建)
角色提示。 另一种与指令提示相关的有趣提示技术是角色提示,它给模型分配一个“角色”或人格。这个角色在提示中通过一个文本片段被分配,例如:
-
你是一位著名且杰出的数学家。
-
你是一名医生。
-
你是一个音乐专家。
有趣的是,最近的 LLM 能够在对话中很好地承担和维持这些角色[18];见下文。
角色提示与 LaMDA(来自[18])
更进一步,角色提示不仅仅是一个有趣的技巧。给 LLM 提供一个角色实际上可以提高性能(例如,将 GPT-3 角色提示为“杰出的数学家”可以提高性能 在基于算术的问题上)。然而,角色提示仅在某些情况下能提高性能。
“当给 AI 分配一个角色时,我们给了它一些背景信息。这个背景帮助 AI 更好地理解问题。理解问题越好,AI 通常会给出更好的答案。” — 来自 learnprompting.org
现实世界中的指令提示。 用指令提示 LLMs 是一个非常强大的工具,我们可以用它来处理各种应用。为了理解如何利用这一技术,我们可以看看最近发布的ChatGPT 插件,其中包括一个开源的信息检索 API。在这个 API 内部,提供了两个特定的模块用于从文档中提取元数据和过滤个人身份信息(PII)。有趣的是,这些服务完全基于 LLM,并使用如下所示的提示。
用于从 ChatGPT 信息检索 API 中提取元数据和检测个人身份信息(由作者创建)
在这些提示中,LLM 获得了有关如何执行期望任务的具体和详细指令。指令的一些显著方面包括:
-
期望的输出格式(无论是 json 还是 true/false)明确说明。
-
指令使用结构化格式(即,以项目符号分隔的列表)来描述重要信息。
-
LLM 的任务(即识别 PII 或提取元数据)在提示中明确说明。
-
有趣的是,这些提示多次告诉模型不要做什么,这通常是不被建议的。
鉴于 LLMs 的局限性,信任 LLM 准确执行如 PII 检测等关键任务可能不是最佳选择。不过,这种方法展示了指令提示的巨大潜力。与其编写整个程序或服务,我们或许可以通过编写一个提示来快速解决许多任务。
重点
“为聊天机器人角色编写一个真正出色的提示是一个极具杠杆效应的技能,并且是用一点自然语言编程的早期示例” — Sam Altman
如果我们从这次概述中学到的其他东西,我们应该知道构造正确的提示(即提示工程)是成功利用 LLMs 的关键部分。由于语言模型的文本到文本结构,它们非常通用,可以用来解决各种任务。然而,我们必须为这些模型提供详细和适当的上下文,以便它们表现良好。尽管最佳提示技术因模型和任务而异,但有许多高层次的要点可以帮助我们最大化成功的机会。
从零到少样本学习。 由于其广泛的预训练(以及如今的微调)数据集,大型语言模型(LLMs)包含大量信息,并能够开箱即用地解决各种任务。为此,我们仅需向模型提供任务描述和相关输入数据,然后模型会生成正确的输出。然而,由于提供给模型的上下文有限,零样本学习的效果有限。为了提升零样本学习的表现,我们应该通过在提示中插入示例来利用少样本学习。
指令跟随 LLMs。 尽管表现良好,少样本学习通常会消耗大量的 tokens,这在大多数 LLMs 的上下文窗口有限的情况下是个问题。为了应对这一点,我们可以采用一种指令提示的方法,提供 LLM 期望行为的精确文本描述,而不是通过正确输出的具体示例来捕捉这种行为。指令提示是强大的,但它需要一种经过特定形式微调的 LLM(例如,通过指令调优或 RLHF)才能良好工作。预训练的 LLMs 在开箱即用时并不擅长跟随指令。
技巧和窍门。 提示工程有各种技巧和最佳实践可以采纳。通常,这些技巧会随着每次新模型发布而变化(例如,GPT-4 在处理非结构化提示方面比之前的模型 [2] 更加出色),但一些原则已经适用了一段时间。首先,我们应该始终从简单的提示开始,然后逐渐增加复杂性。在发展我们的提示时,我们应当尽量具体和详细,同时避免过于冗长(由于有限的上下文窗口)。最后,为了真正最大化 LLM 的性能,我们通常需要利用少样本学习、指令提示或 更复杂的方法。
结束语
非常感谢阅读这篇文章。我是 Cameron R. Wolfe, Rebuy 的 AI 总监。我研究深度学习的实证和理论基础。你也可以查看我在 medium 上的 其他文章!如果你喜欢这篇文章,请关注我的 twitter 或订阅我的 Deep (Learning) Focus 时事通讯,在这里我通过对流行论文的易懂概述,帮助读者深入理解 AI 研究中的主题。
参考文献
[1] Raffel, Colin 等。“使用统一的文本到文本变换器探索迁移学习的极限。” 机器学习研究杂志 21.1 (2020):5485–5551。
[2] Saravia, Elvis 等。“提示工程指南”, github.com/dair-ai/Prompt-Engineering-Guide
(2022)。
[3] Weng, Lilian. (2023 年 3 月)。提示工程。Lil’Log。 lilianweng.github.io/posts/2023-03-15-prompt-engineering/.
[4] Radford, Alec 等。“通过生成性预训练提高语言理解。” (2018)。
[5] Radford, Alec 等。“语言模型是无监督的多任务学习者。”
[6] Brown, Tom 等。“语言模型是少样本学习者。” 神经信息处理系统进展 33 (2020):1877–1901。
[7] Tony Z. Zhao, Eric Wallace, Shi Feng, Dan Klein 和 Sameer Singh. 2021 年。使用前的校准:提高语言模型的少样本性能。ICML。
[8] Su, Hongjin 等。“选择性标注使语言模型成为更好的少样本学习者。” arXiv 预印本 arXiv:2209.01975 (2022)。
[9] Diao, Shizhe 等。“使用思维链的主动提示用于大型语言模型。” arXiv 预印本 arXiv:2302.12246 (2023)。
[10] Liu, Jiachang 等。“什么样的上下文示例对 GPT-$3 $? 有效。” arXiv 预印本 arXiv:2101.06804 (2021)。
[11] Wei, Jason 等。“思维链提示在大型语言模型中引发推理。” arXiv 预印本 arXiv:2201.11903 (2022)。
[12] Wei, Jason, 等. “微调语言模型是零样本学习者。” arXiv 预印本 arXiv:2109.01652 (2021)。
[13] Chen, Mark, 等. “评估训练于代码上的大型语言模型。” arXiv 预印本 arXiv:2107.03374 (2021)。
[14] Ouyang, Long, 等. “通过人类反馈训练语言模型以遵循指令。” 神经信息处理系统进展 35 (2022): 27730–27744。
[15] Touvron, Hugo, 等. “Llama:开放且高效的基础语言模型。” arXiv 预印本 arXiv:2302.13971 (2023)。
[16] Iyer, Srinivasan, 等. “OPT-IML:通过广义化视角扩展语言模型指令元学习。” arXiv 预印本 arXiv:2212.12017 (2022)。
[17] Glaese, Amelia, 等. “通过针对性的人工判断改善对话代理的对齐。” arXiv 预印本 arXiv:2209.14375 (2022)。
[18] Thoppilan, Romal, 等. “Lamda:对话应用的语言模型。” arXiv 预印本 arXiv:2201.08239 (2022)。
实用 Python:spaCy 在 NLP 中的应用
原文:
towardsdatascience.com/practical-python-spacy-for-nlp-b9d626cf53ed
高效的 Python 编程
自然语言处理入门指南
·发表于 Towards Data Science ·12 分钟阅读·2023 年 1 月 9 日
–
spaCy Python 库是一个流行的自然语言处理(NLP)工具。它旨在帮助开发者构建处理和“理解”大量文本的应用程序。spaCy 配备了先进的分词、解析和实体识别功能。它还支持多种流行语言。spaCy 在运行时速度快、效率高,使其成为构建生产级 NLP 应用的良好选择。spaCy 的一个重要部分是其创建和使用特定 NLP 任务的自定义模型的能力,例如命名实体识别或词性标注。开发者可以使用特定于其应用程序的数据进行微调,以满足特定用例的需求。
目录
· 概述
· NLP 和 spaCy 介绍
· 安装和设置 spaCy
· 使用 spaCy 的基本 NLP:分词和词性标注
· 高级 NLP 与 spaCy:NER 和依存句法分析
· 在 spaCy 中处理大型语料库和自定义模型
· 高级 spaCy 技巧:文本分类和词向量
· spaCy 实践
∘ spaCy 和深度学习
∘ spaCy 的功能示例
· 总结:进一步资源和下一步
· 联系
概述
我们介绍了使用 spaCy 进行 NLP 的基础知识,包括分词和词性标注,以及更高级的主题,如命名实体识别和依存句法分析。指南还涉及了处理大型数据集、自定义模型以及文本分类和词向量等高级技巧。最后,它还提供了一些进一步资源的建议以及希望继续学习 spaCy 的 NLP 的下一步。
博客的词云。作者通过www.wordclouds.com/
生成。
NLP 和 spaCy 简介
自然语言处理(NLP)是计算机科学和人工智能的一个领域,致力于使计算机能够理解和处理人类语言(图 2)。NLP 有许多应用,包括机器翻译、文本分类、情感分析和聊天机器人开发。
图 2. NLP 是一个交叉 AI、计算机科学和语言学的话题。视觉效果由作者创建。
在 NLP(自然语言处理)领域中,最受欢迎的 Python 库之一是 spaCy:这是一个开源库,旨在帮助开发者构建能够以速度和效率处理大量文本的应用程序,使其成为构建生产级 NLP 应用程序的不错选择。
安装和设置 spaCy
要安装和设置 spaCy,你必须在计算机上安装 Python 和 pip,即 Python 包管理器。如果你还需要获取 Python 和 pip,你可以从官方网站下载和安装它们。
www.python.org/?source=post_page-----b9d626cf53ed--------------------------------
[## 欢迎访问 Python.org
Python 编程语言的官方网站
一旦你安装了 Python 和 pip,你可以使用 pip 来安装 spaCy。首先,打开终端或命令提示符并输入以下命令:
pip install spacy
这将安装 spaCy 的最新版本及其所有依赖项。
安装后,你必须下载一个 spaCy 的预训练语言模型。spaCy 包括几种语言的模型,包括英语、德语、法语和西班牙语。例如,要下载英语模型,你需要输入以下命令:
python -m spacy download en_core_web_sm
这将下载小型英语模型,其中包括基本的 NLP 功能,如分词、词性标注和依存解析。如果你需要更多高级功能,你还可以下载包含额外功能的大型模型,如命名实体识别和词向量。
基本的 NLP 与 spaCy:分词和词性标注
NLP 中最基本的任务之一是分词,即将文本字符串拆分成单独的标记(词汇和标点符号)。分词在许多 NLP 管道中至关重要,因为它使我们能够将特定的单词和标点符号作为离散的单元进行处理。
spaCy 通过其nlp
对象和Doc
类使得分词变得容易。要使用 spaCy 对文本字符串进行分词,你可以将字符串传递给nlp
对象:
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("This is a sentence.")
返回的Doc
对象包含对象的标记,你可以通过text
访问。例如,你可以像这样迭代这些标记:
for token in doc:
print(token.text)
这将输出输入字符串中的单个标记:
This
is
a
sentence
除了分词之外,另一个常见的 NLP 任务是词性标注(POS tagging),即标记每个词语的对应词性。词性标注通常用于帮助消除词义的歧义,并识别句子的基本语法结构。
spaCy 使得通过 Token
对象的 pos_
属性执行词性标注变得简单。例如,你可以遍历 Doc
对象中的词语,并像这样打印出它们的词性标签:
for token in doc:
print(token.text, token.pos_)
这将输出词语及其词性标签:
This DET
is VERB
a DET
sentence NOUN
spaCy 包含了大量的词性标签,这些标签在所有支持的语言中是一致的。你可以在 spaCy 文档中找到完整的词性标签列表。
在下一节中,我们将探讨如何使用 spaCy 执行更高级的 NLP 任务,如命名实体识别和依存句法分析。
使用 spaCy 进行高级 NLP:命名实体识别和依存句法分析
作者生成的图像。
在前一节中,我们介绍了使用 spaCy 的分词和词性标注的基础知识。在这一节中,我们将探讨两个可以使用 spaCy 执行的更高级的 NLP 任务:命名实体识别和依存句法分析。
命名实体识别(NER)识别并分类文本中的命名实体,如人名、组织名和地点名。NER 通常用于从非结构化文本中提取结构化信息,并且可以成为信息提取和实体链接等任务的宝贵工具。
spaCy 包含了一个预训练的 NER 模型,可以识别和分类文本中的命名实体。要使用 NER 模型,你可以遍历 Doc
对象中的实体,并打印出它们的文本和标签:
for ent in doc.ents:
print(ent.text, ent.label_)
spaCy 包含几个预定义的实体类型,如 PERSON
、ORG
和 GPE
(地缘政治实体)。你可以在 spaCy 文档中找到完整的实体类型列表。
依存句法分析是分析句子的语法结构并确定单词之间的依赖关系。依存句法分析常用于句子的主要主语和宾语,并且可以成为总结和问答等任务的有用工具。
spaCy 包含一个依存句法分析器,可以用来分析句子的语法结构。要使用依存句法分析器,你可以遍历 Doc
对象中的词语,并打印出词语文本、依存标签和头词:
for token in doc:
print(token.text, token.dep_, token.head.text)
这将输出词语、它们的依存标签以及依存关系的头词:
This nsubj is
is ROOT is
a attr a
sentence dobj is
spaCy 包含几个依存标签,这些标签描述了词语与其头词之间的语法关系。你可以在 spaCy 文档中找到完整的依存标签列表。
在下一节中,我们将探讨如何使用 spaCy 处理大数据集并为特定的 NLP 任务构建自定义模型。
在 spaCy 中处理大规模语料库和自定义模型
在前面的章节中,我们涵盖了使用 spaCy 进行分词、词性标注、命名实体识别和依存解析的基础知识。本节将探讨两个更高级的话题:处理大数据集和构建自定义模型。
在处理大数据集时,将整个数据集加载到内存中通常是不切实际的。幸运的是,spaCy 提供了几种工具和技术来以流式方式处理大数据集。
一种方法是使用 spaCy 的 nlp.pipe
方法,它允许你在小批量中处理大数据集。nlp.pipe
方法接受一个文本文档的可迭代对象,并返回处理后的 Doc
对象。例如,你可以像这样使用 nlp.pipe
处理大数据集:
import spacy
nlp = spacy.load("en_core_web_sm")
with open("large_dataset.txt") as f:
for doc in nlp.pipe(f):
# Process the doc here
print(doc)
nlp.pipe
方法旨在高效且节省内存,因此非常适合处理大数据集。
除了处理大数据集外,spaCy 还允许你为特定的自然语言处理任务构建自定义模型。例如,你可以使用 spaCy 的 train
方法在标记数据集上微调一个预训练的模型。这在构建适用于特定领域或语言的模型时非常有用。
要使用 spaCy 训练自定义模型,你必须准备一个训练数据集和配置文件。训练数据集应该是 (text, annotations)
元组的列表,其中 text
是一段文本,annotations
是命名实体注释的字典。配置文件应该指定模型类型、训练数据和配置,以及训练模型的输出目录。
准备好训练数据和配置文件后,你可以使用 spacy.train
方法来训练模型:
import spacy
nlp = spacy.blank("en") # Create a blank English model
# Load the training data and configuration
train_data = [("This is a text.", {"entities": [(4, 10, "ENTITY")]})]
config = {"iterations": 100}
# Train the model
nlp.train(train_data, config)
# Save the trained model to a directory
nlp.to_disk("/model_output_dir")
这让你了解了如何使用 spaCy 处理大数据集和构建自定义模型。接下来的章节将介绍一些高级的 spaCy 技巧,如文本分类和词向量。
高级 spaCy 技巧:文本分类和词向量
高级 spaCy 技巧:文本分类和词向量
在前面的章节中,我们涵盖了使用 spaCy 进行自然语言处理任务的基础知识,如分词、词性标注、命名实体识别和依存解析。本节将探讨两种可以使用 spaCy 执行的更高级的技术:文本分类和词向量。
文本分类是根据文本内容为文本分配标签。文本分类是自然语言处理中的常见任务,可用于各种应用,如情感分析和垃圾邮件检测。
spaCy 包含了几个实用的结构,可用于进行文本分类。一个方法是使用 spaCy 内置的Thinc
库的线性模型训练支持。要使用 spaCy 训练文本分类器,你必须准备一个训练数据集和一个配置文件。训练数据集应为(text, label)
元组的列表,其中text
是文本字符串,label
是目标标签。配置文件应指定模型类型、训练数据和配置,以及训练模型的输出目录。
一旦你准备好训练数据配置文件,就可以使用spacy.train
方法来训练分类器:
import spacy
nlp = spacy.blank("en") # Create a blank English model
# Load the training data and configuration
train_data = [("This is a positive text.", {"cats": {"POSITIVE": 1}}),
("This is a negative text.", {"cats": {"NEGATIVE": 1}})]
词向量是词的数值表示,能够捕捉数据集中词的意义和关系。词向量常用于自然语言处理(NLP),用于表示词之间的相似性,对于文档分类和机器翻译等任务非常有用。
spaCy 包含许多词向量模型,可用于计算数据集的词向量。要在 spaCy 中使用词向量模型,你需要从 spaCy 网站下载一个预训练模型。
一般而言,spaCy 的所有管道包遵循[lang]_[name]的命名约定。对于 spaCy 的…
例如,要下载en_vectors_web_lg
模型,你可以使用以下命令:
python -m spacy download en_core_web_lg
一旦你下载了词向量模型对象,可以使用它来计算数据集的词向量。例如,.vector
访问一个标记的词向量:
import spacy
nlp = spacy.load("en_core_web_lg")
doc = nlp("This is a sentence.")
for token in doc:
print(token.text, '\n', token.vector)
这将输出标记及其词向量:
This [ 1.6849 1.9826 -0.77743 ... -2.9454 -0.83337 ]
is [ 1.4750e+00 6.0078e+00 1.1205e+00 ... -8.5967e-01 9.7466e+00]
a [ -9.3629 9.2761 -7.2708 ... -6.816 3.5737 ]
sentence [-2.7653e+00 -7.9512e-01 ... 1.0124e+00 1.7035e-01]
. [-0.076454 -4.6896 -4.0431 ... -0.52699 -1.3622 ]
每个向量都是一个固定大小的嵌入。
print(len(token.vector))
# 300
spaCy 和深度学习
spaCy 的一个关键特性是它能够使用卷积神经网络(CNNs)执行深度学习任务。这包括命名实体识别(NER)、词性标注(POS tagging)和依赖解析。
要使用 spaCy 的深度学习特性,你需要安装带有en_core_web_md
模型的软件包,其中包括 CNNs。安装后,你可以通过调用 spaCy 对象的方法(如doc
和span
对象)来访问 CNNs 和其他高级特性。
再次,通过终端下载模型:
python -m spacy download en_core_web_md
现在,使用 spaCy 的深度学习特性执行 NER:
import spacy
nlp = spacy.load("en_core_web_md")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")
for ent in doc.ents:
print(ent.text, ent.label_)
这将输出以下内容:
Apple ORG
U.K. GPE
$1 billion MONEY
正如你所见,spaCy 的深度学习特性使得用几行代码就能轻松执行高级 NLP 任务。
spaCy 的功能示例
这里是如何使用 spaCy 的多个不同特性来执行高级自然语言处理任务的示例:
import spacy
# Load the spaCy model with the "en_core_web_md" model
nlp = spacy.load("en_core_web_md")
# Process a text
doc = nlp("Apple is looking at buying U.K. startup for $1 billion. The startup specializes in machine learning and artificial intelligence.")
# Iterate over the named entities in the document
for ent in doc.ents:
# Print the text and label of the entity
print(ent.text, ent.label_)
# Extract the first noun phrase in the text
first_noun_phrase = doc[0:2]
print(first_noun_phrase.text)
# Get the part-of-speech tags for the tokens in the text
pos_tags = [token.pos_ for token in doc]
print(pos_tags)
# Extract the dependencies of the tokens in the text
dependencies = [(token.text, token.dep_, token.head.text) for token in doc]
print(dependencies)
# Get the lemma of the first token in the text
first_token_lemma = doc[0].lemma_
print(first_token_lemma)
输出:
Apple ORG
U.K. GPE
$1 billion MONEY
Apple
['PROPN', 'AUX', 'VERB', 'VERB', 'ADP', 'VERB', 'ADP', 'NOUN', 'NUM', 'NOUN']
[('Apple', 'nsubj', 'is'), ('is', 'aux', 'looking'), ('looking', 'ROOT', 'looking'), ('at', 'prep', 'looking'), ('buying', 'pcomp', 'at'), ('U.K.', 'compound', 'startup'), ('startup', 'dobj', 'buying'), ('for', 'prep', 'buying'), ('$1', 'nummod', 'billion'), ('billion', 'pobj', 'for')]
apple
本示例演示了如何使用 spaCy 的多个不同功能,包括命名实体识别、名词短语提取、词性标注、依存解析和词形还原。
总结:进一步的资源和下一步
本教程涵盖了在 Python 中使用 spaCy 进行自然语言处理的基础知识。随后,我们查看了诸如分词、词性标注、命名实体识别、依存解析等日常 NLP 任务,以及更高级的技术,如文本分类和词向量。
如果你想继续学习有关 spaCy 的 NLP,许多资源可以利用。spaCy 文档是一个很好的起点,其中包括有关库的 API 和功能的详细信息。
spaCy 的核心数据结构是类和对象。Language 类用于处理文本…
你还可以在 spaCy 网站上找到许多教程和示例。
spaCy 与 64 位 CPython 3.6+ 兼容,并且可以在 Unix/Linux、macOS/OS X 和 Windows 上运行。最新的 spaCy 版本…
除了 spaCy 文档,还有许多其他资源可以学习 NLP。以下是一些建议:
- Coursera 上的自然语言处理课程:
## 最佳自然语言处理(NLP)课程与认证 [2023] | Coursera
自然语言处理(NLP)是人工智能(AI)领域的一个分支,专注于使计算机能够…
- Steven Bird、Ewan Klein 和 Edward Loper 合著的《Python 自然语言处理》一书:
[## NLTK 书籍
Steven Bird、Ewan Klein 和 Edward Loper 这本 NLTK 书籍的版本“为 Python 3 和 NLTK 3 进行了更新”。第一…
- Dan Jurafsky 和 James H. Martin 合著的《言语与语言处理》一书
我们列出了许多对改进工作提出了大量精彩建议和修复的优秀人士。
这个教程对你有帮助,并为你使用 spaCy 进行 Python NLP 奠定了坚实的基础。祝你的 NLP 项目好运!
未来的博客将深入探讨在 spaCy 中实现的深度学习机制以及各种可视化 NLP 结果的技术。
如果你有任何进一步的问题或需要更多帮助,请告诉我。
联系方式
想要联系?关注罗宾逊博士在LinkedIn、Twitter、Facebook和Instagram。访问我的主页获取论文、博客、邮件订阅等更多信息!
研究员和企业家问候!作为一名研究员,罗宾逊博士提出并运用了先进的 AI 来理解……
实用的探索性数据分析改进技巧
使 EDA 更简单(和更美观)的简短指南
·
关注 发表在 Towards Data Science · 11 min read · 2023 年 8 月 11 日
–
图片由 Sam Dan Truong 提供,来自 Unsplash
介绍
探索性数据分析(EDA)是使用任何机器学习模型之前的必经步骤。EDA 过程需要数据分析师和数据科学家的专注和耐心:在从分析的数据中获得有意义的洞察之前,通常需要花费大量时间积极使用一个或多个可视化库。
在这篇文章中,我将根据个人经验分享一些如何简化 EDA 过程并提高效率的技巧。特别是,我将给出三条在对抗 EDA 过程中学到的重要建议:
-
使用最适合你任务的非平凡图表;
-
充分利用可视化库的功能;
-
寻找更快的方法来完成相同的任务。
注:为了在这篇文章中创建信息图,我们将使用 Kaggle 上的风电生成数据[2]。我们开始吧!
提示 1:不要害怕使用复杂的图表。
当我在处理与风能分析和预测相关的研究论文时,我学会了如何应用这个技巧[1]。在这个项目的 EDA 过程中,我遇到了需要创建一个总结矩阵的问题,这个矩阵能反映风参数之间的所有关系,以便找出它们之间最强的相互影响。第一个想到的想法是建立一个我在许多数据科学/数据分析项目中看到的‘老牌’相关矩阵。
如你所知,相关矩阵用于量化和总结变量之间的线性关系。在以下代码片段中,corrcoef
函数用于风电生成数据的特征列。这里我还应用了 Seaborn 的heatmap
函数,将相关矩阵数组绘制为热图:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
cols = ['P', 'Ws', 'Power_curve', 'Wa']
# build the matrix
correlation_matrix = np.corrcoef(data[cols].values.T)
hm = sns.heatmap(correlation_matrix,
cbar=True, annot=True, square=True, fmt='.3f',
annot_kws={'size': 15},
cmap='Blues',
yticklabels=['P', 'Ws', 'Power_curve', 'Wa'],
xticklabels=['P', 'Ws', 'Power_curve', 'Wa'])
# save the figure
plt.savefig('image.png', dpi=600, bbox_inches='tight')
plt.show()
相关矩阵的示例。图像由作者提供。
分析结果图表后,可以得出风速和有功功率之间有强相关性,但我认为许多人会同意我的观点,即使用这种可视化方式解释结果并不容易,因为这里只展示了数字。
一个良好的替代相关矩阵的方案是散点图矩阵,它允许你在一个地方可视化数据集中不同特征之间的成对相关性。在这种情况下,应该使用 sns.pairplot
:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
cols = ['P', 'Ws', 'Power_curve', 'Wa']
# build the matrix
sns.pairplot(data[cols], height=2.5)
plt.tight_layout()
# save the figure
plt.savefig('image2.png', dpi=600, bbox_inches='tight')
plt.show()
散点图矩阵的示例。图像由作者提供。
通过查看散点图矩阵,可以快速观察数据的分布情况以及是否包含异常值。然而,这种图表的主要缺点与成对绘制数据造成的重复有关。
最后,我决定将上述图表合并为一张,其中左下部分包含所选参数的散点图,右上部分包含不同大小和颜色的气泡:较大的圆圈表示所研究的参数具有更强的线性相关性。矩阵的对角线将显示每个特征的分布:这里的窄峰值表明该参数变化不大,而其他特征则变化较大。
构建此总结矩阵的代码如下。这里的图包括三部分——fig.map_lower
、fig.map_diag
、fig.map_upper
:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
cols = ['P', 'Ws', 'Power_curve', 'Wa']
# buid the matrix
def correlation_dots(*args, **kwargs):
corr_r = args[0].corr(args[1], 'pearson')
ax = plt.gca()
ax.set_axis_off()
marker_size = abs(corr_r) * 3000
ax.scatter([.5], [.5], marker_size,
[corr_r], alpha=0.5,
cmap = 'Blues',
vmin = -1, vmax = 1,
transform = ax.transAxes)
font_size = abs(corr_r) * 40 + 5
sns.set(style = 'white', font_scale = 1.6)
fig = sns.PairGrid(data, aspect = 1.4, diag_sharey = False)
fig.map_lower(sns.regplot)
fig.map_diag(sns.histplot)
fig.map_upper(correlation_dots)
# save the figure
plt.savefig('image3.jpg', dpi = 600, bbox_inches = 'tight')
plt.show()
总结矩阵的示例。图片由作者提供。
总结矩阵结合了之前研究的两种图表的优点——它的下半部分(左侧)模拟散点图矩阵,上半部分(右侧)图形化地反映了相关矩阵的数值结果。
提示 2:充分利用可视化库的功能
我时常需要向同事和客户展示 EDA 的结果,因此可视化是我在这项任务中重要的助手。我总是尽量在图表中添加各种元素,如箭头和注释,以使其更具吸引力和可读性。
让我们回到前面讨论的风电项目的 EDA 实施案例。当谈到风能时,功率曲线是最重要的参数之一。风力涡轮机(或整个风电场)的功率曲线是一个图表,展示了在不同风速下生成的电力。需要注意的是,涡轮机在低风速下不会运行。它们的启动与切入速度相关,通常在 2.5–5 m/s 的范围内。风速在 12 到 15 m/s 之间时,达到额定功率。最后,每台涡轮机都有一个上限风速,当风速超过此限值时,涡轮机将停止发电,直到风速降回到操作范围内。
研究的数据集包括理论功率曲线(这是来自制造商的典型曲线,没有任何离群点)和实际曲线(如果我们绘制风力与风速的关系,后者通常包含许多超出理想理论形状的点,这可能是由于涡轮机故障、不正确的 SCADA 测量或计划外维护所致)。
现在我们将创建一张图像,展示两种类型的功率曲线——首先是没有任何额外项(除了图例)的:
import pandas as pd
import matplotlib.pyplot as plt
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
# build the plot
plt.scatter(data['Ws'], data['P'], color='steelblue', marker='+', label='actual')
plt.scatter(data['Ws'], data['Power_curve'], color='black', label='theoretical')
plt.xlabel('Wind Speed')
plt.ylabel('Power')
plt.legend(loc='best')
# save the figure
plt.savefig('image4.png', dpi=600, bbox_inches='tight')
plt.show()
一张‘静默’的风力功率曲线图。图片由作者提供。
如你所见,这张图需要解释,因为它没有包含任何额外的细节。
但如果我们添加线条来突出显示图表的三个主要区域——标记了切入、额定和切出速度的区域,以及一个箭头注释来展示一个离群点会怎样呢?
让我们看看在这种情况下图表会是什么样子:
import pandas as pd
import matplotlib.pyplot as plt
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
# build the plot
plt.scatter(data['Ws'], data['P'], color='steelblue', marker='+', label='actual')
plt.scatter(data['Ws'], data['Power_curve'], color='black', label='theoretical')
# add vertical lines, text notes and arrow
plt.vlines(x=3.05, ymin=10, ymax=350, lw=3, color='black')
plt.text(1.1, 355, r"cut-in", fontsize=15)
plt.vlines(x=12.5, ymin=3000, ymax=3500, lw=3, color='black')
plt.text(13.5, 2850, r"nominal", fontsize=15)
plt.vlines(x=24.5, ymin=3080, ymax=3550, lw=3, color='black')
plt.text(21.5, 2900, r"cut-out", fontsize=15)
plt.annotate('outlier!', xy=(18.4,1805), xytext=(21.5,2050),
arrowprops={'color':'red'})
plt.xlabel('Wind Speed')
plt.ylabel('Power')
plt.legend(loc='best')
# save the figure
plt.savefig('image4_2.png', dpi=600, bbox_inches='tight')
plt.show()
一张‘多话’的风力曲线图。图像由作者提供。
提示 3:总是找到更快的方法来完成相同的工作
在分析风数据时,我们通常希望获得有关风能潜力的全面信息。因此,除了风能的动态,还需要一个图表显示风速如何依赖于风向。
为了展示风力的变化,可以使用以下代码:
import pandas as pd
import matplotlib.pyplot as plt
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
# resample 10-min data into hourly time measurements
data['Date/Time'] = pd.to_datetime(data['Date/Time'])
fig = plt.figure(figsize=(10,8))
group_data = (data.set_index('Date/Time')).resample('H')['P'].sum()
# plot wind power dynamics
group_data.plot(kind='line')
plt.ylabel('Power')
plt.xlabel('Date/Time')
plt.title('Power generation (resampled to 1 hour)')
# save the figure
plt.savefig('wind_power.png', dpi=600, bbox_inches='tight')
plt.show()
下面是结果图:
风力的动态。图像由作者提供。
如人们可能注意到的那样,风力动力学的轮廓具有相当复杂、不规则的形状。
风玫瑰,或称极坐标玫瑰图,是一种特殊的图表,用于表示气象数据的分布,通常是按方向划分的风速 [3]。matplotlib
库中有一个简单的模块windrose
,可以轻松构建这种可视化图表,例如:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from windrose import WindroseAxes
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
wd = data['Wa']
ws = data['Ws']
# plot normalized wind rose in a form of a stacked histogram
ax = WindroseAxes.from_ax()
ax.bar(wd, ws, normed=True, opening=0.8, edgecolor='white')
ax.set_legend()
# save the figure
plt.savefig('windrose.png', dpi = 600, bbox_inches = 'tight')
plt.show()
基于现有数据获得的风玫瑰图。图像由作者提供。
看着风玫瑰图,可以注意到有两个主要的风向——东北和西南。
但是如何将这两张图片合并为一张呢?最明显的选择是使用add_subplot
。尽管由于windrose
库的特殊性,这不是一个简单的任务:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from windrose import WindroseAxes
# read data
data = pd.read_csv('T1.csv')
print(data)
# rename columns to make their titles shorter
data.rename(columns={'LV ActivePower (kW)':'P',
'Wind Speed (m/s)':'Ws',
'Theoretical_Power_Curve (KWh)':'Power_curve',
'Wind Direction (°)': 'Wa'},inplace=True)
data['Date/Time'] = pd.to_datetime(data['Date/Time'])
fig = plt.figure(figsize=(10,8))
# plot both plots as subplots
ax1 = fig.add_subplot(211)
group_data = (data.set_index('Date/Time')).resample('H')['P'].sum()
group_data.plot(kind='line')
ax1.set_ylabel('Power')
ax1.set_xlabel('Date/Time')
ax1.set_title('Power generation (resampled to 1 hour)')
ax2 = fig.add_subplot(212, projection='windrose')
wd = data['Wa']
ws = data['Ws']
ax = WindroseAxes.from_ax()
ax2.bar(wd, ws, normed=True, opening=0.8, edgecolor='white')
ax2.set_legend()
# save the figure
plt.savefig('image5.png', dpi=600, bbox_inches='tight')
plt.show()
在这种情况下,结果如下所示:
一张风力动力学和风玫瑰图的合成图。图像由作者提供。
这里的主要缺点是两个子图的大小不同,因此风玫瑰图周围有很多空白区域。
为了简化操作,我建议采取不同的方法,使用Python Imaging Library
(PIL) [4],仅需 11 (!) 行代码:
import numpy as np
import PIL
from PIL import Image
# list images that needs to be merged
list_im = ['wind_power.png','windrose.png']
imgs = [PIL.Image.open(i) for i in list_im]
# resize all images to match the smallest
min_shape = sorted([(np.sum(i.size), i.size) for i in imgs])[0][1]
# for a vertical stacking - we use vstack
images_comb = np.vstack((np.asarray(i.resize(min_shape)) for i in imgs))
images_comb = PIL.Image.fromarray(imgs_comb)
# save the figure
imgages_comb.save('image5_2.png', dpi=(600,600))
这里的输出看起来更美观,因为两张图片大小相同,代码选择了最小的一张,并将其他图片调整为匹配:
一张使用 PIL 获得的风力动力学和风玫瑰图。图像由作者提供。
顺便提一下,在使用PIL
时,也可以使用水平堆叠——例如,让我们比较和对比一个‘沉默’的和一个‘多话’的功率曲线图:
import numpy as np
import PIL
from PIL import Image
list_im = ['image4.png','image4_2.png']
imgs = [PIL.Image.open(i) for i in list_im]
# pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here)
min_shape = sorted([(np.sum(i.size), i.size) for i in imgs])[0][1]
imgs_comb = np.hstack((np.asarray(i.resize(min_shape)) for i in imgs))
### save that beautiful picture
imgs_comb = PIL.Image.fromarray(imgs_comb)
imgs_comb.save('image4_merged.png', dpi=(600,600))
比较和对比两个功率曲线图。图像由作者提供。
结论
在这篇文章中,我与您分享了三个使 EDA 过程更轻松的技巧。希望这些建议对您有所帮助,并且您也能将它们应用到自己的数据任务中。
这些提示完美地匹配了我在进行 EDA 时总是尝试应用的公式:定制 → 列出 → 优化。
好吧,你可能会问,这到底为什么重要?我可以说,实际上这很重要,因为:
-
根据你当前面临的具体需求定制图表是非常重要的。例如,不要创建大量的信息图表,想想如何将几个图表合并成一个,就像我们在创建总结矩阵时一样,这样可以结合散点图和相关图表的优点。
-
你所有的图表都应该能自我说明。因此,你需要知道如何在图表上列出重要信息,使其详细且易于阅读。比较一下“沉默”和“健谈”的功率曲线之间的差异。
-
最后,每个数据专家都应该学习如何优化 EDA 过程以提高效率(让生活更轻松)。如果你需要将两张图像合并为一张,并不一定要一直使用
add_subplot
选项。
还有什么呢?我可以肯定地说,EDA 是处理数据时非常有创意和有趣的一步(更不用说它也非常重要)。
让你的信息图表像钻石一样闪耀,不要忘记享受这个过程!
参考列表
-
论文《数据驱动的风能分析与预测应用:“La Haute Borne”风电场的案例》。
doi.org/10.1016/j.dche.2022.100048
-
风力发电数据:
www.kaggle.com/datasets/bhavikjikadara/wind-power-generated-data?resource=download
-
关于 windrose 库的教程:
windrose.readthedocs.io/en/latest/index.html
感谢阅读!
“实际”构建图像分类器
以实际方式构建图像分类器(基于真实故事)
·
关注 发表在 Towards Data Science ·10 min read·2023 年 1 月 17 日
–
图片由 Elena Mozhvilo 提供,发布在 Unsplash
大多数从业者首先通过图像分类学习深度学习。如果你上过编程课程,那么可以将这视为他们开始时给你的“hello world”练习。尽管许多专家认为图像分类问题已经解决(这可能有一定的道理),但从这类问题中你仍然可以学到很多细微之处和技巧。话虽如此,在接下来的几个版本中,我将通过讲故事的形式分享我如何构建、评估和分析这些模型。起初,我考虑将所有内容整合到一篇文章中。但是为了让你免于阅读我对托尔斯泰《战争与和平》的回应,我将其分解成若干部分。注意: 本文中使用的所有代码都可以在这里找到。
那么你认为你能进行分类吗?
为了激励这个问题,让我们假设你被聘为 Petpoo Inc 的计算机视觉(CV)工程师,这是一家宠物洗发水公司。现在你可能会问,为什么一家宠物洗发水公司需要一名计算机视觉工程师。更重要的是,为什么他们会叫自己 Petpoo?我将在下面回答第一个问题,但老实说,我不知道第二个问题的答案。
在你的第一天,你的经理见到你并带你熟悉你的项目。
“嗨 INSERT_YOUR_NAME_HERE!我们很高兴你能加入我们。Petpoo 正在扩大规模,我们客户最希望的功能之一是为他们的宠物推荐定制洗发水。我们的客户对社交媒体非常熟悉,希望他们的宠物明星在分享的短视频和自拍中拥有最佳造型。他们也没有时间填写冗长的问卷,因为他们忙着教他们的宠物拍出完美的鸭脸。所以我们正在开发一种基于照片识别宠物的解决方案。一旦我们知道了宠物的品种,我们就可以从我们的产品线中推荐适合的定制洗发水,让宠物和它的主人都开心。”
“由于我们大多数客户拥有猫或狗,我们希望你能构建一个图像分类器,以识别宠物是猫还是狗。作为开始,咱们先保持在这个层次。根据我们上线后的反馈,我们可以为解决方案添加更多功能。祝好运,我们都寄望于你。”
你问:“我们有些什么数据用于原型设计?”
“好问题。你可以从这个数据集开始。”他回复道。
“我们将如何评估性能?”你问道。
“对于原型,使用你最好的判断。”他说完便离开了。
好的,你需要构建一个图像分类器来区分猫和狗。你有一个数据集可以开始使用,还需要弄清楚如何评估原型。
你决定先查看数据。毕竟,如果数据不好,再多的机器学习洗发水也无济于事。
初看起来,这些数据处理得当,你可以立即开始使用。总共有近 7500 张图像,因此应该足够用于微调。数据集包含了特定宠物品种的分类标签,所以你需要对其进行修改,使每张图像都与猫或狗标签相关联。好的,写一个简单的函数来完成这个任务,我们就准备好了。
示例图像和标签:图像由作者创建
所以,猫的图像将有标签“True”,而狗的图像将有标签“False”。
在开始实验之前,你将数据分为训练集和验证集。你足够聪明地知道,简单性总是优于复杂性。因此,作为第一步,你选择了一个知名的深度学习模型——ResNet-18 作为基线。然后,你关闭了所有高级设置,比如调度器、数据增强、预训练权重和正则化,并通过它传递一批数据。当你检查模型的预测时,你看到的是这样的结果:
(TensorBase([0.0056, 0.9944]), TensorBase(1.))
很好,概率加起来为 1。这意味着基本设置是正确的。感到自信,你训练模型几个轮次,并观察发生了什么。损失似乎在减少,但对于这样一个简单的问题和如此干净的数据集,准确率却让人失望。
10 轮训练后的结果(未使用迁移学习):图像由作者创建
训练曲线:图像由作者创建
为了留下良好的第一印象,你可视化了被错误分类的示例,并绘制了混淆矩阵。
分类结果:图像由作者创建
错误分类示例:图像由作者创建
嗯,模型似乎将大量的狗误认为是猫。奇怪,非常奇怪。
你推测这是因为训练示例不足以从头开始训练。因此,你决定使用一种在你的职业生涯中表现良好的技术——迁移学习。
在用预训练权重初始化 Resnet-18 后,你重复了相同的实验,保持其他一切不变。这次的结果看起来很棒。几乎没有错误,训练曲线也非常好。
10 轮训练后的结果(使用迁移学习):图像由作者创建
使用迁移学习的训练曲线:图像由作者创建
你重复之前的可视化,发现只有少量的狗被误认为是猫。
混淆矩阵看起来好多了!图像由作者创建
错误分类示例:图像由作者创建
高兴的是你的新工作顺利开始,你保存了所有工作并在傍晚回家。
很久以后。凌晨 1:30……你被惊醒,汗珠从额头上滑落。
你在设置实验时错过了一个非常重要的细节。准确率可能根本不是这个问题的好指标。
为什么?
第二天,你急忙赶到工作岗位,检查你的直觉是否正确。在检查样本图像并计算数据集的总大小时,由于急于获得结果,你忘记了检查猫和狗的图像数量。数据集最初设计用于分类不同品种的狗和猫。它可能为了这个任务而进行了平衡(每个类别有相似数量的样本)。
你从未检查猫和狗的图像数量是否相似。你在键盘上急速打字,键盘的“哒哒”声后随之而来的沉寂证实了你的怀疑。
**猫:**2400 张,占总数据的 32.4%
**狗:**4990 张,占总数据的 67.6%
这解释了为什么你训练的第一个模型效果很差。数据不平衡使模型混淆。数据集中狗的数量是猫的两倍多。模型无法收集到足够的信息来区分猫和狗,因为它看到的大多数图像都是狗。但是为什么你训练的第二个模型几乎完美无瑕?因为你用训练了 120 多万张图像的权重初始化了它,其中许多是狗和猫的图像。
问题解决了!没问题吧?其实不然。迁移学习(你在第二个模型中做的)并不总是有效。当预训练数据集(模型最初训练时的数据)和目标数据集(你正在训练的数据)之间存在领域不匹配时,迁移学习可能会失败。你很幸运,因为预训练数据集与你正在使用的宠物数据集有相似的分布(呼~)。
下面是下一个错误——默认将准确率作为指标。将混淆矩阵并排查看。左侧是你的原始实验,右侧是迁移学习实验。供参考,行是实际情况。第一行对应实际的狗图像,第二行对应实际的猫图像。第一列代表模型认为是狗的图像,第二列代表模型认为是猫的图像。
混淆矩阵并排对比:由作者创建的图像
如果你查看了原始的准确率数字,你会看到在没有迁移学习的情况下准确率为 86%,而在进行迁移学习后准确率为 99.7%。还不错,对吧?
然而,在左侧的混淆矩阵中,你可以看到 44 只狗被模型分类为猫,162 只猫被分类为狗。这是一个你会因盲目依赖准确率而错过的大问题。这是为什么?
想象一下一个最坏的情况,如果你只有 90 张狗的图片和 10 张猫的图片,并且没有预训练的权重。如果你把所有 100 张图片的答案都猜为狗,你的准确率将是 90%。你也会得到一个完全无用的模型。
因此,在另一种情况下,使用准确率作为指标将隐藏模型中的这一根本性缺陷,直到为时已晚。我的意思是,如果哈里斯·皮尔顿这位喜爱小狗的社交名人使用了这个模型,发现它无用,然后在她的社交媒体渠道上谴责了这个产品,那该怎么办?
当你感谢幸运星并整合迁移学习的结果时,你的经理过来问道。“你好啊!你的工作进展如何?”,他问。“还不错,我想我有一些成果。”,你紧张地回答。你向他展示了你的迁移学习成果以及 0.003 的误差率。他感到惊讶。“我们多久可以为此制作一个演示?”,他问。“我可以在本周开始着手准备”,你回答。
“我们的营销团队与顶级网红哈里斯·皮尔顿有联系,我们希望他们尝试一下这个并给我们反馈。”他咧嘴一笑。“嗯……好的”,你吞了吞口水,回忆起你之前的失误。“好的,我们目标是下周末前。快点!”,他说着拍了拍你的肩膀,愉快地走开了。
笔在纸上划过的涂鸦伴随着沉默的停顿。即使你正在设计应用程序,你的思绪却游离在别处。我应该用什么来评估模型?我该如何平衡这个不稳定的数据集?为什么我会遗漏这些基本步骤?
在接下来的几天里,你构建了一个简单的应用程序界面,以测试模型并查看它在真实世界图像上的表现。毕竟,如果你无法将模型用于实际应用,训练它有什么意义呢?
对初步原型感到满意后,你将注意力转向脑海中那些迫切的问题。翻开一本参考教材,你浏览了 F1 分数、ROC、精准度和召回率,所有这些想法涌上心头。你重新运行了实验,但除了测量准确率外,你还测量了精准度和召回率。结果现在更加有意义——在没有迁移学习的模型中,精准度和召回率较低,而准确率掩盖了这些缺陷。另一方面,迁移学习模型的精准度和召回率都非常高。
没有迁移学习——一般的精准度和召回率:由作者创建的图像
有了迁移学习——出色的精准度和召回率:由作者创建的图像
你决定写一个检查清单,说明如何训练模型并评估它——以备你或团队中的其他人在未来需要参考。
但在你开始之前,你的经理过来了。你向他展示了正在开发中的应用,他非常高兴。“这真棒!我们的营销团队与一些影响者进行了交谈,他们希望这个应用能更具个性化——按品种推荐。我试图说服他们,但他们已经承诺在下周末影响者来总部时准备好。你认为到那时能做好吗?我知道这将需要一些艰苦的工程工作。”他说得很歉意。“细粒度分类?到下周末?这要求很高。我可以找个人和我一起工作吗?”你对突然变化的要求和缩短的时间表感到惊讶。“嗯……”他停顿了一下……
(本系列的结论将在 2023 年某个时候发布——仍在前期制作中)
这是我给你的问题:
你会如何处理这个新任务?考虑到这些限制,什么是最佳方法?你需要注意什么?
没有正确的答案,只有权衡。请给我发个消息或在此帖子中留下评论,分享你的想法。
数据集详情:
牛津宠物数据集 — 该数据集可用于商业/研究目的下载,受 知识共享署名-相同方式共享 4.0 国际许可协议 保护。版权归图像的原始所有者所有。
预训练上下文是你所需的一切
原文:
towardsdatascience.com/pre-training-context-is-all-you-need-f457ffa8a358
现代变换模型背后的驱动力在很大程度上来源于其相关数据,从而允许强大的上下文学习能力。
·发布于 Towards Data Science ·6 分钟阅读·2023 年 11 月 27 日
–
生成型人工智能及其流行的变换模型如今随处可见,每小时都有新模型发布(见人工智能的膨胀)。在这个迅速发展的人工智能领域,这些模型可能带来的价值似乎是无穷无尽的。像chatGPT这样的“大型语言模型”(LLM)已经成为每位工程师资源的组成部分,作家们用它们来支持他们的文章,设计师们则用计算机视觉模型的结果来创建初步的视觉效果或寻找灵感。
如果这不是魔法,那究竟是什么驱动了这些令人印象深刻的变换模型?
然而,即使成就和实用性都很出色,生成型人工智能提升了生产力,但重要的是要记住,现代机器学习模型(如 LLM 或 Vision Transformers)并没有进行任何魔法(类似于 ML 或统计模型本身从未有过神奇的事实)。尽管模型的卓越能力可能被视为类似魔法,一些领域专家甚至谈到模型的幻觉,但每个模型的基础仍然只是数学和统计概率(虽然有时复杂,但仍是数学)。这引出了一个根本性的问题:如果这不是魔法,那究竟是什么驱动了这些令人印象深刻的变换模型?
图 1:展示了 ChatGPT(使用 GPT4)将其“先进技术”和“广泛训练”视为主要性能驱动因素。
每个模型的基础是数据
与任何模型(统计模型或机器学习模型)一样,训练数据对模型的最终性能影响最大。如果没有大量高质量的数据来反映你希望模型学习的关系,那么就没有可以训练的内容,最终的模型表现会很差(著名的 GIGO 原则:垃圾进垃圾出)。这一数据建模的基本原则多年来没有改变。每个革命性的新型变换模型背后首先只有一个因素:数据。是数据的数量、质量和上下文决定了模型的后续表现。近期研究(见下文)通过展示最新的生成性人工智能模型在提供的上下文属于训练分布时能够很好地进行泛化,而在异质分布学习中表现不佳来支持这一点。
同质分布与异质分布学习
重要的是要记住,模型不过是一个巨大的网络、树或关系图。机器学习模型基本上学习的是如何将给定的输入转换为所需的输出(见图 2)。
图 2:一个超级简单的神经网络结构图,它根据天气和其他上下文预测人流量。在左侧是训练过程中的输入(特征),而右侧是输出(目标)。在它们之间可以有多个转换(层),这些层学习复杂的输入输出关系。
当模型被训练时(换句话说:当这些关系被更新时),输入的上下文和输出的信息量将决定模型的擅长领域。类似于人类能够很好地回答母语中的问题,机器学习模型在处理它们见过的数据时表现较好。这被称为同质分布学习。如果在训练过程中,模型接收到大量丰富的上下文,它可以依赖于这种获得的知识,最终的预测结果会显示出准确的表现。
然而,异质分布学习描述的是模型需要基于它未曾见过的上下文进行预测的情况。你可以想象一个从未学过挪威语的人突然回应用挪威语提出的问题。请查看图 3 以了解同质分布和异质分布学习的概况。
图 3:展示了同质分布(左)与异质分布(右)学习。左侧的模型在面对不属于原始训练数据的新的上下文(在这种情况下是“政治”)时表现较差,而右侧的模型在处理未见过的上下文时表现良好。机器学习模型通常属于左侧类别,并且在异质分布学习中表现不佳。
现代 LLMs 和其他 ML 模型的出色性能来源于原始训练数据中的大量上下文。由于模型的广泛预训练,能够处理的内分布学习的问题范围非常广泛。这使得模型能够回答各种问题,这可能对用户而言看起来像是魔法或人类级智能,但实际上并非如此。同样,模型的错误或意外回答也并不是真正的幻觉,它基本上突出了原始训练数据中的上下文缺口,从而导致分布外学习。总的来说,机器学习模型在其分布外学习能力上非常有限,需要对基础模型进行广泛训练。
语言模型中的预训练力量
在谷歌 DeepMind 成员最近的一篇论文中,作者进一步支持了现代 LLMs 的上下文学习性能主要源自其预训练分布的观点。论文“Pretraining Data Mixtures Enable Narrow Model Selection Capabilities in Transformer Models”由Steve Yadlowsky、Lyric Doshi和Nilesh Tripuraneni(2023)撰写,专注于现代变换器模型如何获得其令人印象深刻的上下文学习能力(即应对任何提供的上下文的能力)。
变换器模型,特别是大型语言模型(LLMs),具有在上下文中学习的卓越能力…
doi.org](https://doi.org/10.48550/arXiv.2311.00871?source=post_page-----f457ffa8a358--------------------------------)
这些发现非常有启发性。当变换器模型在覆盖广泛上下文的数据上进行预训练时,它们在学习新的任务时表现出令人印象深刻的能力,这些任务都属于预训练上下文。这种能力接近最优,展示了在训练分布内的显著泛化和适应能力。然而,当这些模型遇到超出其预训练领域的上下文时,性能受到限制且会出现失败。这突显了在分布外上下文中的减少泛化能力和明显限制。
视觉变换器:规模案例研究
在另一项研究中(也是由谷歌 DeepMind 于 2023 年进行)题为:“ConvNets Match Vision Transformers at Scale”的论文中,作者Samuel L. Smith、Andrew Brock、Leonard Berrada和Soham De挑战了一个在计算机视觉领域广泛存在的观念,即在大规模下,现代的视觉变换器模型超越了传统的模型,如卷积神经网络(CNNs)。该研究使用类似的计算预算训练了 CNNs 和视觉变换器,并比较了它们的性能。
## ConvNets 在规模上匹敌视觉 Transformer
许多研究人员认为,卷积神经网络(ConvNets)在小型或中型数据集上表现良好,但在更大的数据集上不具竞争力……
结果表明,预训练所使用的计算预算与随后的性能之间存在规模法则。在 ImageNet 上进行微调后,预训练的 CNN 在相当的预算下达到了与视觉 Transformer 相匹配的性能。
摘要
这两项研究共同描绘了现代 Transformer 模型令人印象深刻的性能。首先,性能不仅仅受到模型架构的驱动,更主要是由预训练的量驱动。其次,当预训练的上下文涵盖广泛时,生成的模型也会展示出广泛的上下文学习能力。
这些研究强调了一个关键原则:训练数据的量、质量和上下文是任何基础机器学习模型最关键的部分。在不知道预训练涵盖的上下文的情况下,很难事先确定模型在哪些领域表现良好。基准测试可以帮助指示潜在的上下文限制。这些测试并不展示模型的一般性能,它们主要展示了哪些上下文曾经成为模型训练分布的一部分。
总之,随着人工智能时代的到来和数据科学家及工程师在机器学习模型开发中的人数增加,预训练涵盖广泛上下文的重要性变得越来越明显;在许多方面,它不仅是过程的一部分,而且是你所需要的全部。
所有图像,除非另有说明,均由作者提供。
请查看 我的个人主页,关注我,或 订阅我的邮件列表 ,如果你想了解我写的内容或希望及时获得新故事的更新。
精准聚类简化版:kscorer 的自动选择最佳 K-means 聚类指南
kscorer 简化了聚类过程,通过先进的评分和并行化提供了实用的数据分析方法
·
关注 发表在 Towards Data Science · 7 分钟阅读·2023 年 11 月 10 日
–
由 DALL-E-2 根据作者的描述制作
无监督机器学习,尤其是聚类,在数据科学中是一项具有挑战性的任务。它对广泛的实际商业分析项目至关重要。聚类可以独立运行,但它也是复杂数据处理管道中的一个有价值的组成部分,这些管道提升了其他算法的效率。例如,聚类在开发推荐系统时起着关键作用。
好吧,Scikit-Learn 著名地提供了各种经过验证的聚类算法。尽管如此,其中大多数都是参数化的,需要设置簇数,这是聚类中最重要的挑战之一。
通常,使用迭代方法来决定最佳的簇数。这意味着你多次进行聚类,每次使用不同的簇数,并评估相应的结果。虽然这种技术很有用,但也有其局限性。
yellowbrick 包是一个常用工具,可以轻松识别最佳簇数。然而,它也有一些缺点。一个显著的缺点是评估多个指标时可能会出现冲突的结果,以及在图表上识别肘部的挑战。
此外,数据集的大小也是一个问题,不论使用何种包。当处理大数据集时,资源消耗困难可能会妨碍你有效地迭代多个簇。如果是这种情况,可以考虑探索诸如MiniBatchKMeans等技术,它可以实现并行聚类。
但对你的聚类程序进行高级优化可能需要一些鲜为人知的技术,稍后会详细介绍。你还将了解kscorer 包,它简化了这些技术,提供了一种更强大和高效的方法来确定最佳簇数。
这些技术毫无疑问是:
-
降维。 在应用聚类算法之前,对数据进行主成分分析(PCA)可能会有益。这将减少数据干扰,并导致更可靠的聚类过程。
-
余弦相似度。 有一种简单的方法可以在 K-means 中使用(近似)余弦距离,即通过对数据进行欧几里得归一化。这样你就不需要预先计算距离矩阵,比如在进行凝聚聚类时。
-
多指标手头。 为了找到最佳簇数,应该依赖多指标评估,而不是仅仅依靠单一指标。
-
数据采样。 为了解决资源消耗问题并改进聚类结果,可以从数据中获取随机样本以执行聚类操作并评估指标。通过多次迭代的平均得分可以减少随机性的影响,从而产生更一致的结果。
下面展示了这个工作流程。
图片由作者提供
幸运的是,无需从头开始构建整个管道,因为 kscorer 包中已经有现成的实现。
现在,让我们深入探讨一下
我曾在一次会议演讲中听到一位数据科学家说:“基本上,只要你知道自己在做什么,你可以做任何你想做的事情。” © Alex2006
在聚类之前建议对数据进行缩放,以确保所有特征处于同一水平,避免因特征的大小而主导。标准化(以均值为中心并按标准差缩放)或最小-最大缩放(将值缩放到指定范围)是常用的缩放技术。
值得注意的是,特征缩放的重要性在这里得到了完美的说明,它不仅限于 KNeighbors 模型,还适用于各种数据科学方法。通过 z-score 归一化对特征进行标准化可以确保所有特征在相同的尺度上,防止任何特征因其大小而主导模型调整。这个缩放过程可能会显著影响模型的性能,与使用未缩放数据时进行的模型调整相比,可能会导致不同的模型调整。
此外,K-means 聚类与**主成分分析(PCA)**之间存在基本联系,这在丁和赫的论文 “K-means Clustering via Principal Component Analysis”中进行了探讨。尽管最初这两种技术的目的不同,但最终它们都旨在有效地表示数据,同时最小化重构误差。PCA 旨在将数据向量表示为减少数量的特征向量的组合。而 K-means 聚类则旨在将数据向量表示为簇中心向量的组合。这两种方法都力求最小化均方重构误差。
在应用 PCA 后,由于可能出现的计算问题(有些值可能接近零,而其他值可能非常大),我们需要再次对数据进行缩放。这完全有意义,因为我们在 PCA 后已经失去了对初始特征的跟踪,因此数据将无法解释。
另一个可能不为人知的有趣相关性是余弦相似度与欧几里得距离之间的关系。理解这种关系在这些度量间接互换使用时至关重要。这些知识在将传统的 K-means 聚类算法转换为球面 K-means 聚类算法时具有实际应用,其中余弦相似度是聚类数据的关键指标。如前所述,我们可以通过对数据应用欧几里得归一化来“建立”余弦相似度与欧几里得距离之间的联系。
在缺乏真实簇标签的情况下,聚类模型的评估必须依赖于内在度量,而kscorer 包提供了一套全面的指标来评估聚类质量。这些指标提供了有关识别簇之间分离程度的有价值见解:
-
轮廓系数。它通过计算数据点到其不属于的最近簇的平均距离与每个数据点的簇内平均距离之间的差异来量化簇的分离程度。结果经过标准化,并表示为两者之间的比例,值越高表示簇分离越优越。
-
Calinski-Harabasz 指数。它计算簇间散度与簇内散度的比率。Calinski-Harabasz 测试得分越高,表示聚类性能越好,簇定义越清晰。
-
Davies-Bouldin 指数。它衡量簇间离散度与簇内离散度的比率,值越低表示聚类性能越优越,簇的区分度越高。
-
邓恩指数。它通过比较簇间距离(任意两个簇质心之间的最小距离)与簇内距离(簇内任意两点之间的最大距离)来评估簇的质量。邓恩指数越高,表示簇定义越清晰。
包中使用的指标的 Python 计算方法如下:
- 贝叶斯信息准则 (BIC)。BIC 作为一个额外的、在某种程度上是独立的度量。虽然 K-means 没有提供直接的概率模型,但 BIC 可以帮助估计应用 K-means 模型后的数据分布。这种方法提供了对簇质量更全面的评估。
所有指标都经过标准化,确保较高的评分始终表示定义明确的聚类。这种彻底的评估对于识别数据集中最佳聚类数至关重要。
为了克服记忆限制,并迅速执行数据预处理和评分操作以进行 K-means 聚类,kscorer 包利用 N 个随机数据样本。这种方法确保了无缝执行,并能适应不同大小和结构的数据集。类似于交叉验证技术,它能够保持稳健的结果,即使每次迭代只关注数据的一个有限子集。
使用 kscorer 动手操作
因此,我们有一些数据用于聚类。请注意,我们在此场景中假设不知道确切的聚类数。
接下来,我们将把数据集分成训练集和测试集,并拟合一个模型来检测最佳聚类数。该模型将自动在 3 到 15 之间搜索最佳聚类数。这可以轻松实现,如下所示:
完成拟合过程后,我们可以查看所有应用指标的标准化评分。这将帮助我们确定适合我们数据的最佳聚类数。当查看图表时,你会注意到一些聚类被突出显示,并带有相应的评分。这些标记点对应于所有指标的平均评分中的局部最大值,因此代表了选择最佳聚类数的最佳选项。
现在,我们可以评估我们新的聚类标签与真实标签的匹配程度。请确保这种选项在实际商业场景中通常是不可用的😉
在聚类中,你可以尝试对之前未见过的数据进行聚类。但请注意,这并不是一个典型的聚类任务。一种不同且通常更有用的策略是使用聚类标签作为目标来构建分类器。这将使得将聚类标签分配给新数据变得更容易。
最后,提供一个新的互动视角来观察我们的数据。
因此,这就是我们如何利用kscorer 包深入研究 K-means 聚类,该包简化了寻找最佳聚类数的过程。由于其复杂的指标和并行处理,它已被证明是一个实用的数据分析工具。
使用 Python 预测人类表现的极限
原文:
towardsdatascience.com/predict-the-limits-of-human-performance-with-python-50cc824d2539
使用 SciPy 建模指数衰减
·发表于 Towards Data Science ·10 分钟阅读·2023 年 5 月 1 日
–
一幅表现奥运跑者冲过终点线的生动油画,描绘成星云的爆炸(由 DALL-E2 制作)
人类会不会跑得比法拉利快?当然不会。人类表现本质上是有限的,许多因素限制了我们的速度,包括血液输送氧气的速度和肌肉的反应速度。除非我们进行重大基因工程,否则我们现在的速度就是我们能达到的极限。
那么我们怎么知道这一点呢?事实上,人类表现,像许多其他特质一样,遵循一个钟形曲线分布[1]。这意味着大多数人都在曲线的峰值附近的平均范围内,只有少数人特别慢或特别快。当我们离峰值越来越远时,具有这种表现水平的个体数量会以指数方式下降。在短跑的情况下,这意味着最快的短跑运动员已经达到了曲线的平坦、收缩部分。因此,取得显著的速度提升将变得越来越困难。
2005 年以来记录的 100 米试验时间图,低于 9.93 秒[2]。(参考麦考密克工程学院(2016))
这并不是说没有改进的空间。训练、营养、设备和其他因素可以帮助个人在其基因限制范围内提高表现。然而,重要的是要认识到,我们不能从根本上改变我们的身体极限,尤其是当监管机构继续限制在田径比赛中使用先进的生物技术时。
由于人类表现是有限的,我们可以预测许多运动项目的未来结果[3]。例如,100 米短跑世界纪录的减少,像许多其他自然现象一样,似乎遵循指数衰减的模式[4]。因此,我们可以用指数方程来建模:
指数方程(图片由作者提供)
在这个方程中,y
表示对世界纪录短跑时间的预测(以秒为单位);x
表示自首次记录设立以来的年数;a
、b
和c
表示曲线拟合参数:
-
a
是尺度因子或幅度。它决定了指数函数的垂直伸展或压缩。 -
b
是衰减常数。它表示当x
增加时,函数衰减的速度。 -
c
是垂直位移的量。它决定了水平渐近线的y
值,即当x
趋近于无穷大时,指数函数接近的一条水平线。
这三个参数是通过将指数函数拟合到一组数据点来解决的,使用的是曲线拟合算法。通过最小化y
的预测值与每个x
值处实际y
值之间差异的平方和,来确定最适合数据的值。
当然,如果你不是数学家,找到正确的参数可能会有些困难。幸运的是,对于我们其他人来说,有一个 Python 库可以让曲线优化变得非常简单。
SciPy 库
开源的SciPy 库在 NumPy 的基础上扩展,提供了物理常数、转换因子和用于数学、科学及工程的数值例程[5]。这些包括用于曲线拟合的优化例程,这正是我们项目所需要的。
使用 conda 安装 SciPy:
conda install scipy
使用 pip 安装:
pip install scipy
代码
该项目的 Python 代码是在 JupyterLab 中编写的。如果你想下载笔记本,可以在这个Gist找到。
导入库并设置 RC 参数
以下单元导入库并为 matplotlib 图形设置运行配置参数。提前设置这些参数不是严格必要的,但在绘制多个图形时可以减少后续代码量。
import warnings
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import scipy.optimize
# Suppress warnings for using np.exp() on large values during optimization:
warnings.filterwarnings('ignore')
# Set default run configuration for plots:
plt.rcParams['figure.figsize'] = (6, 4)
plt.rc('font', size=12)
plt.rc('axes', titlesize=14)
plt.rc('axes', labelsize=12)
plt.rc('xtick', labelsize=11)
plt.rc('ytick', labelsize=11)
plt.rc('legend', fontsize=11)
加载数据
世界纪录不经常被打破,因此匹配的数量少于二十个。我们将使用topend sports 网站上的纪录列表,并将其作为字典[6]输入。然后我们将字典转换为 pandas DataFrame,以便于使用。我们的大部分分析将使用纪录(以秒为单位)与自首次记录以来的年数,因此我们将添加一个表示年数的列。
# Input men's 100 m world records in seconds.
# If two records were set in the same year, list only the latest (lowest):
records = {2009: 9.58, 2008: 9.69, 2007: 9.74, 2005: 9.77, 2002: 9.78,
1999: 9.79, 1996: 9.84, 1994: 9.85, 1991: 9.86, 1988: 9.92,
1983: 9.93, 1968: 9.95, 1960: 10, 1956: 10.1, 1936: 10.2,
1930: 10.3, 1921: 10.4, 1912: 10.6}
# Turn dictionary into a DataFrame:
df = pd.DataFrame(records.items(), columns=['year', 'time'])
df['years'] = df['year'] - 1912 # Years since first record.
df = df.sort_values('year').reset_index(drop=True)
display(df)
男子 100 米世界纪录数据框(作者提供的图片)
绘制世界纪录
尽早查看数据总是一个好主意,因此让我们从数据框中制作一个茎状图。这应该可以轻松地查看数据中的趋势和异常值。
# Graph the world records:
plt.stem(df.year, df.time)
plt.title("Men's 100 m Sprint World Records")
plt.ylabel("Time (secs)")
plt.ylim(9.5, 10.8)
plt.grid(True);
按年份绘制的男子 100 米世界纪录茎状图(作者提供的图片)
正如你可能对指数衰减的预期,纪录时间最初下降得相当快,但随后开始趋于平稳,就像飞机接近跑道一样。虽然现代运动员拥有优化的训练、营养和设备,但随着接近人类表现极限,进步变得越来越困难。或者并非如此?
看一下右侧的最后两个数据点。它们看起来像是从悬崖上掉下来的。这不是一个温和的曲线平稳着陆。这很奇怪。这就是乌塞恩·博尔特。
牙买加跑者蒙太奇,由 DALL-E2 生成(提示:一幅戏剧性的油画,描绘一位穿着黄色衬衫和绿色短裤的牙买加奥运跑者,冲过终点线的画面如星云爆炸)
乌塞恩·博尔特的疯狂故事
乌塞恩·博尔特是一位牙买加跑者,拥有“世界上最快的人”称号[6][7]。2008 年,他在男子 100 米短跑中以 9.69 秒的时间赢得了奥运金牌。这创造了新的世界纪录,尽管他在庆祝时提前放慢了速度(你可以在这里观看)。
一年后,博尔特保持专注,以 9.58 秒的时间和 44.72 公里/小时(27.79 英里/小时)的最高速度越过终点线。这个纪录比生物统计学家基于当时数学模型的预期早了几十年。
今天,多亏了博尔特,对于 100 米的最终时间预测充满了谦逊和不确定性。在讨论的数字中,包括相对较高的 9.44 秒和非常低的9.27 [8][9]。
为了评估博尔特对 100 米冲刺预测的影响,我们来做一些自己的预测。这些预测将完全基于以前的世界纪录,而不是所有职业比赛的结果。我们将首先预测没有博尔特的情况,然后重复这个过程有博尔特的情况。由于我们会做多次预测,因此我们将首先编写用于创建和优化指数函数的函数。
定义指数衰减的函数
第一个函数将接受x
值(自第一个记录以来的年数)和曲线拟合参数a
、b
、c
,并返回预测的y
值(时间)。第二个函数将接受第一个函数作为参数,以及x
和y
数据,并使用 SciPy 的[optimize.curve_fit()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html#scipy.optimize.curve_fit)
方法自动选择最佳拟合参数。我们将p0
参数设置为None
,这意味着我们让方法决定y
的最佳最小值和最大值,而不是提供猜测。
def expo(x, a, b, c):
"""Return y values for exponential decay curve."""
return a * np.exp(-b * x) + c
def optimize_curve_fit(a_func, x, y):
"""Return optimized parameters for curve fit."""
params, covar = scipy.optimize.curve_fit(a_func, x, y, p0=None)
return params
优化曲线拟合参数
在调用我们的函数之前,我们需要构建包含博尔特时间(_all
后缀)和不包含博尔特时间(_nB
后缀)的世界纪录数据集。我们将这些数据集连同我们的expo()
函数一起传递给optimize_curve_fit()
函数。该函数返回优化后的a
、b
和c
拟合参数作为 NumPy 数组。
# Generate datasets with and without Bolt's times (nB = No Bolt):
x_all, y_all = df.years, df.time
x_nB, y_nB = x_all[:-2], y_all[:-2]
# Find optimized parameters for fitting the curve to the points:
params_nB = optimize_curve_fit(expo, x_nB, y_nB)
params_all = optimize_curve_fit(expo, x_all, y_all)
print(f"Parameters without Bolt (a, b, c) = {params_nB}")
print(f" Parameters with Bolt (a, b, c) = {params_all}")
Parameters without Bolt (a, b, c) = [0.98795896 0.01631187 9.57391395]
Parameters with Bolt (a, b, c) = [1.34836526 0.00941746 9.18654695]
绘制结果
为了绘制我们的预测曲线,我们将把我们的指数衰减函数(expo()
)传递给 matplotlib 的plot()
方法,并使用优化后的拟合参数。
# Plot exponential curves for data with and without Bolt's times:
plt.plot(x_all, y_all, '.', label='measured data', c='k')
plt.plot(x_nB, expo(x_nB, *params_nB),
'-', label='fitted without Bolt')
plt.plot(x_all, expo(x_all, *params_all), '--',
label='fitted with Bolt', c='red')
plt.title("Men's 100 m World Record")
plt.xlabel('Years Since First Record (1912)')
plt.ylabel('Times (s)')
plt.grid(True)
plt.legend(framealpha=1);
两条拟合世界纪录数据的指数曲线(作者提供的图像)
哇。尤塞恩·博尔特真的是超出了曲线。这是因为,从物理上讲,他是一个异常值。尽管比大多数短跑运动员高且步幅更长,但他仍能保持类似的步频,这意味着他可以用更少的步伐覆盖相同的距离。这可能是因为他结合了较矮短跑运动员的快肌纤维和较高个子人的身体机械优势[8]。
一个有效的问题是他是否打破了曲线,还是仅仅加快了我们沿曲线的进展。要调查这一点,我们需要将两条曲线外推到未来。
预测未来表现
以下代码首先将指数曲线外推 570 年(从 1912 年首次记录前 20 年到 550 年后)。绘制曲线后,标记博尔特的数据点,以便我们可以看到它们在未来与曲线交点的位置。最后,打印每条曲线预测的最小时间。请注意,这些值与曲线拟合练习中的c
参数相同。
# Extrapolate exponential curves to predict future performance:
x_extrap = np.arange(-20, 550)
y_nB_extrap = expo(x_extrap, *params_nB) # Without Bolt.
y_B_extrap = expo(x_extrap, *params_all) # With Bolt.
# Create a plot of the world record times and the extrapolated curves.
fig, ax = plt.subplots()
ax.plot(x_all, y_all, '.', label='data', c='k')
ax.plot(x_extrap, y_nB_extrap, '-', label='fitted without Bolt')
ax.plot(x_extrap, y_B_extrap, '--', c='red', label='fitted with Bolt')
ax.set(title="Men's 100 m World Record Extrapolated",
xlabel='Years Since First Record (1912)',
ylabel='Time (s)',
yticks=np.arange(9.0, 11.0, 0.2))
ax.grid(True)
ax.legend(framealpha=1)
# Add a dotted horizontal line for each of Bolt's world record times.
bolt_times = {2009: 9.58, 2008: 9.69}
for year, time in bolt_times.items():
ax.axhline(time, ls=':', linewidth=1.3, color='red')
ax.text(0, time + 0.01, f"Bolt {year}", color='red',
horizontalalignment='left', size=9)
# Define function and inverse function to permit a secondary x-axis for year:
axis_transform = lambda x_extrap: x_extrap + 1912
axis_inverse = lambda x_extrap: x_extrap - 1912
ax2 = ax.secondary_xaxis('top', functions=(axis_transform, axis_inverse))
print(f"\nMinimum predicted time without Bolt data = {min(y_nB_extrap):.2f} sec.")
print(f"Minimum predicted time with Bolt data = {min(y_B_extrap):.2f} sec.\n")
Minimum predicted time without Bolt data = 9.57 sec.
Minimum predicted time with Bolt data = 9.19 sec.
外推到未来的两条曲线(作者提供的图像)
从技术上讲,这两条曲线都允许博尔特当前的 9.58 秒记录。如果我们假设红色曲线包含博尔特的数据,并且提供了有效的预测,那么博尔特的成就就提前了数十年。
红色曲线预测,100 米冲刺的终极人类极限为 9.19 秒,并将在约 400 年内达到。虽然 9.19 秒确实很快,但这并不与其他已发布的预测值相悖,如9.27、9.26和9.09秒 [9][10][11]。
虽然 400 年是很长时间,但一些研究人员认为博尔特的当前纪录将再保持约230 年 [2]!无论如何,我们的 9.57 秒和 9.19 秒的值都是合理的,有很大可能夹住终极值。数学(以及 Python)真是太神奇了!
摘要
许多自然现象,如放射性衰变、岩石断裂和人口增长,可以使用指数方程、幂律和逻辑斯蒂函数等工具进行数学建模。除了匹配现有数据,这些模型还可以预测未来行为。在这个快速成功数据科学项目中,我们使用了指数方程来预测男子 100 米赛跑的终极跑步时间。
拟合曲线到数据需要操作多个参数。目标是最小化实际数据点和预测数据点之间的误差。Python 的 SciPy 库包括自动化这一过程的函数,使曲线拟合对所有人都易于实现。
来源
[1] 正态分布。 (2023 年 4 月 16 日)。在维基百科。 en.wikipedia.org/wiki/Normal_distribution
。
[2] 西北大学麦考密克工程学院:打破博尔特 100 米纪录需要多长时间?Luis Amaral 教授计算了几率 (2016)。
[3] Little, Brown and Company, 公式:成功的普遍法则 作者 Albert-László Barabási (2018)。
[4] 指数衰减。 (2023 年 3 月 11 日)。在维基百科。
[5] SciPy: https://scipy.org/ (2023)。
[6] Robert Wood, “100 米世界纪录。” Topend Sports 网站,2008,www.topendsports.com/sport/athletics/record-100m.htm,
访问日期 2023 年 5 月 1 日。
[7] 尤赛恩·博尔特。 (2023 年 4 月 15 日)。在维基百科。
[8] Wired: 博尔特飞速无比,但远未接近人类极限 作者 Alexis Madrigal (2008)。
[9] Runner’s World: 终极 100 米时间:9.27 秒? 作者:安比·伯富特 (2014)。
[10] Idea & Issac: Femto Essays: 男子 100 米世界纪录挑战简单曲线拟合 作者:田端达夫 (2008)。
[11] Idea & Issac: Femto Essays: 博尔特的世界纪录再次改变了经验预测 作者:田端达夫 (2009)。
谢谢!
感谢阅读,并确保关注我,以获取未来更多的快速成功数据科学项目。