一、说明

        作为 Python 和 ML 的初学者,我经常依赖 scikit-learn 完成几乎所有项目。它的简单性和多功能性使实现各种算法成为一种迷人的体验。现在,令人兴奋的是,scikit-learn 通过“Scikit-LLM”引入了 LLM 功能,从而得到了进一步发展。这种集成将 GPT、Vertex、Gemma、Mistral、Llama 等大型语言模型的强大功能带入了 scikit-learn 生态系统,使其成为机器学习爱好者和专业人士更强大的工具。根据该库的文档,“Scikit-LLM 允许您将强大的语言模型无缝集成到 scikit-learn 中,以增强文本分析任务。”

注意:本文中的所有代码示例均取自Scikit-LLM的文档

二、关于LLM

        在 Scikit-LLM 中,估算器根据其使用的语言模型 API 类型进行分组,这称为“后端系列”。此分组基于API 格式,而不是语言模型的实际架构。例如,任何遵循 OpenAI API 格式的模型都归类为 GPT 系列,无论使用哪种提供程序或模型架构。每个系列都有自己的一组估算器,位于特定的子模块中。

        例如,零样本分类器可在不同的后端系列下使用:如ZeroShotGPTClassifierGPT 系列和ZeroShotVertexClassifierVertex 系列。当不同的提供商向其模型添加其他提供商可能不支持的独特功能时,将这些后端系列分开可以提供灵活性。尽管如此,为了确保易用性,系列数量仍保持在最低限度。

        除非另有说明,否则将使用系列的默认后端。例如,OpenAI 后端是 GPT 系列的默认后端,但用户可以通过在模型设置中指定它来切换到 Azure 后端。但是,并非所有模型都支持每个后端。

三、GPT 家族

        GPT 系列包含所有使用 OpenAI API 格式的后端,OpenAI 后端为默认后端。如果没有提及特定后端,则使用 OpenAI 后端。要启用它,只需设置您的 OpenAI API 密钥和组织 ID。

from skllm.config import SKLLMConfig

SKLLMConfig.set_openai_key("<YOUR_KEY>")
SKLLMConfig.set_openai_org("<YOUR_ORGANIZATION_ID>")

还可以通过 Azure OpenAI 服务访问 OpenAI 模型。要使用 Azure 后端,请提供您的 Azure API 密钥和终结点。

from skllm.config import SKLLMConfig
# Found under: Resource Management (Left Sidebar) -> Keys and Endpoint -> KEY 1
SKLLMConfig.set_gpt_key("<YOUR_KEY>")
# Found under: Resource Management (Left Sidebar) -> Keys and Endpoint -> Endpoint
SKLLMConfig.set_azure_api_base("<API_BASE>") # e.g. https://<YOUR_PROJECT_NAME>.openai.azure.com/

        使用 Azure 后端时,模型应指定为model = "azure::<model_deployment_name>"。例如,如果我们以名称my-model创建了gpt-3.5部署,则应使用。model = "azure::my-model"

3.1 GGUF

        GGUF 是一种用于存储量化模型权重和配置的开源格式。它主要用于 Llama CPP 项目,但也可以由其他运行时加载。要将 GGUF 模型与 scikit-llm 一起使用,请安装 llama-cpp 库及其 Python 绑定,安装命令因硬件而异。

来自文档

所有模型均使用量化版本,精度通过后缀表示(例如,q4 表示 4 位)。默认情况下,使用 4 位量化模型,但可能提供具有不同精度的模型。选择模型时,请考虑以下经验法则:
q < 4:性能损失显著,尺寸较小
q = 4:损失与尺寸之间的最佳权衡
4 < q < 8:性能损失最小,尺寸较大
q = 8:几乎没有性能损失,尺寸非常大

3.2 GPU 加速

GGUF 模型可以全部或部分卸载到 GPU,并使用命令设置最大 GPU 层数。

from skllm.config import SKLLMConfig

SKLLMConfig.set_gguf_max_gpu_layers(-1)

0:所有层都在 CPU 上
-1:所有层都在 GPU 上
n>0:n 层在 GPU 上,其余层在 CPU 上

请注意,更改此设置不会自动重新加载模型。要从内存中卸载模型,请使用以下命令。

from skllm.llm.gpt.clients.llama_cpp.handler import ModelCache

ModelCache.clear()

这在 Jupyter Notebook 等交互式环境中很有用,因为模型会一直保留在内存中直到过程结束。

3.3 自定义 URL

        自定义 URL 后端支持使用任何 GPT 估算器和与 OpenAI 兼容的提供商,无论是本地还是基于云的。设置全局自定义 URL 以使用此后端。

from skllm.config import SKLLMConfig

SKLLMConfig.set_gpt_url("http://localhost:8000/")

clf = ZeroShotGPTClassifier(model="custom_url::<custom_model_name>")

        注意,如果在同一个脚本中同时使用自定义 URL 和 OpenAI 后端,请使用重置自定义 URL 配置SKLLMConfig.reset_gpt_url()

四、Vertex 家族

Vertex 系列有一个后端,即 Google Vertex AI。要使用它,请配置您的 Google Cloud 凭据:

  1. 在 Google Cloud Console 中创建一个 Google Cloud 项目。
  2. 从列表中选择项目。
  3. 搜索并选择 Vertex AI。
  4. 安装 Google Cloud CLI 并使用官方文档中的命令设置默认凭据。
gcloud auth application-default login

5. 使用您的项目 ID 配置 Scikit-LLM:

from skllm.config import SKLLMConfig

SKLLMConfig.set_google_project("<YOUR_PROJECT_ID>")

要在 Vertex 中调整 LLM,您需要 64 个 TPU v3 pod 核心,默认为 0。要增加此配额:

  1. 转到配额并过滤“每个区域的受限图像训练 TPU V3 pod 核心”。
  2. 选择“europe-west4”区域。
  3. 点击“编辑配额”,将限制设置为 64,然后提交申请。审批可能需要几小时到几天的时间。

第三方集成

scikit-ollama :Scikit-Ollama 提供 scikit-llm 估算器,允许通过Ollama使用自托管 LLM 。

五、文本分类

5.1.零样本文本分类

        LLM 的一个关键特性是,只要标签具有描述性,它就能对文本进行分类而无需重新训练。例如,要将文本分类为 [正面、负面、中性] 等类别,我们可以使用ZeroShotGPTClassifier常规 scikit-learn API。

from skllm.models.gpt.classification.zero_shot import ZeroShotGPTClassifier
from skllm.datasets import get_classification_dataset

# demo sentiment analysis dataset
# labels: positive, negative, neutral
X, y = get_classification_dataset()

clf = ZeroShotGPTClassifier(model="gpt-3.5-turbo")
clf.fit(X,y)
labels = clf.predict(X)

在零样本设置中,不需要训练数据;您只需提供候选标签列表。

from skllm.models.gpt.classification.zero_shot import ZeroShotGPTClassifier
from skllm.datasets import get_classification_dataset

X, _ = get_classification_dataset()

clf = ZeroShotGPTClassifier()
clf.fit(None, ["positive", "negative", "neutral"])
labels = clf.predict(X)

您还可以执行多标签分类,为单个文本分配多个标签。

from skllm.models.gpt.classification.zero_shot import MultiLabelZeroShotGPTClassifier
from skllm.datasets import get_multilabel_classification_dataset

X, _ = get_multilabel_classification_dataset()
candidate_labels = [
    "Quality",
    "Price",
    "Delivery",
    "Service",
    "Product Variety",
    "Customer Support",
    "Packaging",
    "User Experience",
    "Return Policy",
    "Product Information",
]
clf = MultiLabelZeroShotGPTClassifier(max_labels=3)
clf.fit(None, [candidate_labels])
labels = clf.predict(X)

注意:零样本分类器的性能在很大程度上取决于标签的描述性和清晰度。例如,不要使用“语义”,而要使用更自然的短语,如“所提供文本的语义是<<语义>>”。

API 参考

# Zero Shot GPT Classifier
from skllm.models.gpt.classification.zero_shot import ZeroShotGPTClassifier

# Multi Label Zero Shot GPT Classifier
from skllm.models.gpt.classification.zero_shot import MultiLabelZeroShotGPTClassifer

# Zero Shot Vertex Classifier
from skllm.models.vertex.classification.zero_shot import ZeroShotVertexClassifier

# Multi Label Zero Shot Vertex Classifier
from skllm.models.vertex.classification.zero_shot import MultiLabelZeroShotVertexClassif

5.2. 小样本文本分类

        少量样本文本分类涉及根据每个类别的几个示例将文本归类为预定义类别(例如正面、负面、中性)。Scikit-LLM 估算器使用整个训练集来创建示例,因此如果您的数据集很大,请考虑将其拆分为较小的训练集和验证集。建议每个类别保留不超过 10 个示例,并对数据进行混洗以避免近期偏差。

from skllm.models.gpt.classification.few_shot import (
FewShotGPTClassifier,
MultiLabelFewShotGPTClassifier,
)
from skllm.datasets import (
    get_classification_dataset,
    get_multilabel_classification_dataset,
)

# single label
X, y = get_classification_dataset()
clf = FewShotGPTClassifier(model="gpt-4o")
clf.fit(X,y)
labels = clf.predict(X)

# multi-label
X, y = get_multilabel_classification_dataset()
clf = MultiLabelFewShotGPTClassifier(max_labels=2, model="gpt-4o")
clf.fit(X,y)
labels = clf.predict(X)

API 参考

# Few Shot GPT Classifier
from skllm.models.gpt.classification.few_shot import FewShotGPTClassifier

# Multi Label Few Shot GPT 
from skllm.models.gpt.classification.few_shot import MultiLabelFewShotGPTClassifier

5.3.动态小样本文本分类

        动态少样本分类扩展了少样本文本分类,使其更适合处理较大的数据集。它不使用固定的示例集,而是为每个样本动态选择一个子集,从而优化模型的上下文窗口并节省标记。它不使用固定的示例集,而是动态地为每个样本选择一个子集,从而优化模型的上下文窗口并保存标记。

        例如,如果目标是将评论分类为关于一本书或一部电影,则训练集可能包含 6 个样本,每个类别 3 个。

X = [
    "I love reading science fiction novels, they transport me to other worlds.", # example 1 - book - sci-fi
    "A good mystery novel keeps me guessing until the very end.", # example 2 - book - mystery
    "Historical novels give me a sense of different times and places.", # example 3 - book - historical
    "I love watching science fiction movies, they transport me to other galaxies.", # example 4 - movie - sci-fi
    "A good mystery movie keeps me on the edge of my seat.", # example 5 - movie - mystery
    "Historical movies offer a glimpse into the past.", # example 6 - movie - historical
]

y = ["books", "books", "books", "movies", "movies", "movies"]

        现在我们需要对这条评论进行分类,“我深深地爱上了这本科幻小说;它独特的科学与虚构的融合让我着迷。”由于查询是关于科幻小说的,我们希望使用最相关的示例 1 和 4 进行分类。当使用每个类别 1 个示例的动态少样本分类器时,模型正确地选择示例 1 和 4 作为最相关的示例。

from skllm.models.gpt.classification.few_shot import DynamicFewShotGPTClassifier

query = "I have fallen deeply in love with this sci-fi book; its unique blend of science and fiction has me spellbound."

clf = DynamicFewShotGPTClassifier(n_examples=1).fit(X,y)

prompt = clf._get_prompt(query)
print(prompt)
...

Sample input:
"I love reading science fiction novels, they transport me to other worlds."

Sample target: books


Sample input:
"I love watching science fiction movies, they transport me to other galaxies."

Sample target: movies

...

        此过程使用 KNN 搜索算法作为预处理器。首先,使用嵌入模型对训练集进行矢量化。然后,使用 Scikit-Learn KNN 或 Annoy 等算法构建最近邻索引,以高效查找相关示例。为了确保类别平衡,训练数据按类别划分,允许在少样本提示期间从每个类别中平等采样 N 个示例。

API 参考

# Dynamic Few Shot GPT Classifier
from skllm.models.gpt.classification.few_shot import DynamicFewShotGPTClassifier

5.4. 思路链文本分类

        CoT 文本分类与零样本分类类似,但该模型还会生成标签选择背后的推理。这可以提高性能,但会增加 token 消耗。

from skllm.models.gpt.classification.zero_shot import CoTGPTClassifier
from skllm.datasets import get_classification_dataset

# demo sentiment analysis dataset
# labels: positive, negative, neutral
X, y = get_classification_dataset()

clf = CoTGPTClassifier(model="gpt-4o")
clf.fit(X,y)
predictions = clf.predict(X)
labels, reasoning = predictions[:, 0], predictions[:, 1]

API 参考

# CoT GPT Classifier
from skllm.models.gpt.classification.zero_shot import CoTGPTClassifier

5.5.可调文本分类

可调式估算器可让您微调底层 LLM 以完成分类任务,这些任务通常在云端执行(如 OpenAI 或 Vertex),因此无需本地 GPU。但是,调整可能既昂贵又耗时。建议首先尝试上下文学习估算器,并且仅在必要时使用可调式估算器。

from skllm.models.gpt.classification.tunable import GPTClassifier

X, y = get_classification_dataset()
clf = GPTClassifier(n_epochs=1)
clf.fit(X,y)
clf.predict(X)

API 参考

# GPT Classifier
from skllm.models.gpt.classification.tunable import GPTClassifier

# Multi Label GPT Classifier
from skllm.models.gpt.classification.tunable import MultiLabelGPTClassifier

# Vertex Classifier
from skllm.models.vertex.classification.tunable import VertexClassifier

六、文本到文本建模

6.1.文本摘要

        LLM 擅长总结任务。Scikit-LLM 提供了一个总结器,它既可以用作独立的估计器,也可以用作预处理器,类似于降维。

from skllm.models.gpt.text2text.summarization import GPTSummarizer
from skllm.datasets import get_summarization_dataset

X = get_summarization_dataset()
summarizer = GPTSummarizer(model="gpt-3.5-turbo", max_words=15)
X_summarized = summarizer.fit_transform(X)

        超参数max_words对字数设置了软限制,但实际数量可能略微超过此限制。您还可以使用可选focus参数生成专注于特定概念的摘要。

summarizer = GPTSummarizer(model="gpt-3.5-turbo", max_words=15, focus="apples")

API 参考

# GPT Summarizer
from skllm.models.gpt.text2text.summarization import GPTSummarizer

6.2. 文本翻译

        LLM 擅长翻译任务。Scikit-LLM 包含一个 Translator 模块,用于将文本翻译成目标语言。

from skllm.models.gpt.text2text.translation import GPTTranslator
from skllm.datasets import get_translation_dataset

X = get_translation_dataset()
t = GPTTranslator(model="gpt-3.5-turbo", output_language="English")
translated_text = t.fit_transform(X)

API 参考

# GPT Translator
from skllm.models.gpt.text2text.translation import GPTTranslator

6.3. 可调文本到文本

        可调式文本到文本估算器可针对摘要、问答和翻译等任务进行自定义。它们按原样使用数据,无需额外的预处理或提示构造,因此用户必须确保数据格式正确。

from skllm.models.gpt.text2text.tunable import TunableGPTText2Text

model = TunableGPTText2Text(
        base_model = "gpt-3.5-turbo-0613",
        n_epochs = None, # int or None. When None, will be determined automatically by OpenAI
        system_msg = "You are a text processing model."
)

model.fit(X_train, y_train) # y_train is any desired output text
out = model.transform(X_test)

API 参考

# unable GPT Text2Text
from skllm.models.gpt.text2text.tunable import TunableGPTText2Text

# Tunable Vertex Text2Text
from skllm.models.vertex.text2text.tunable import TunableVertexText2Text

七、其他任务

7.1.文本矢量化

        LLM 可以通过将文本转换为固定维度的向量来预处理数据,然后可将其与各种模型(例如分类、回归、聚类)一起使用。

# Embedding the text
from skllm.models.gpt.vectorization import GPTVectorizer

vectorizer = GPTVectorizer(batch_size=2)
X = vectorizer.fit_transform(["This is a text", "This is another text"])

# Combining the vectorizer with the XGBoost classifier in a sklearn pipeline
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoder
from xgboost import XGBClassifier

le = LabelEncoder()
y_train_encoded = le.fit_transform(y_train)
y_test_encoded = le.transform(y_test)

steps = [("GPT", GPTVectorizer()), ("Clf", XGBClassifier())]
clf = Pipeline(steps)
clf.fit(X_train, y_train_encoded)
yh = clf.predict(X_test)

API 参考

# GPT Vectorizer
from skllm.models.gpt.vectorization import GPTVectorizer

7.2. 标记

        Scikit-LLM 中的标记可以是一个任意任务,它采用原始文本并返回带有插入的类似 XML 的标记的相同文本。

# Input
I love my new phone, but I am disappointed with the battery life.

# Output
<positive>I love my new phone,</positive> <negative>but I am disappointed with the battery life.</negative>

        在理想情况下,这种标注过程应该是可逆的,因此原始文本总是可以从标注文本中重建出来。然而,这并不总是可行的,因此不被视为强制性要求。

7.3.命名实体识别

        NER 是 Scikit-LLM 中的一项实验性功能,可能不稳定。NER 涉及定位和分类文本中的命名实体。目前,Scikit-LLM 提供了一个 NER 估算器“可解释 NER”,仅适用于 GPT 系列。

from skllm.models.gpt.tagging.ner import GPTExplainableNER as NER

entities = {
  "PERSON": "A name of an individual.",
  "ORGANIZATION": "A name of a company.",
  "DATE": "A specific time reference."
}

data = [
  "Tim Cook announced new Apple products in San Francisco on June 3, 2022.",
  "Elon Musk visited the Tesla factory in Austin on January 10, 2021.",
  "Mark Zuckerberg introduced Facebook Metaverse in Silicon Valley on May 5, 2023."
]

ner = NER(entities=entities, display_predictions=True)
tagged = ner.fit_transform(data)

        该模型标记实体并为每个实体提供简要的推理。如果display_predictions设置为 True,则输出将自动解析并以可读格式显示:实体会突出显示,并且将鼠标悬停在实体上时会出现解释。

        稀疏与密集 NER
        生成 NER 预测有两种模式:稀疏和密集。

        密集模式:立即生成完全标记的输出。当使用较大的模型(例如 GPT-4)或文本中有多个词汇歧义词时,该模式非常有用。例如,在“Apple 是 Apple 首席执行官最喜欢的水果”这句话中区分“Apple”的不同实例。

        稀疏模式:通过正则表达式生成映射到文本的实体列表。它通常是首选,因为:

  • - 输出标记较少(经济高效)。
  • - 保证可逆性和实体特定输出。
  • - 模型较小,准确率较高。

一般建议使用稀疏模式,除非特定条件下需要密集模式。

API 参考

# GPT ExplainableNER
from skllm.models.gpt.tagging.ner import GPTExplainableNER

八、结论

        Scikit-LLM 通过集成用于各种 NLP 任务(包括文本分类、摘要和翻译)的高级语言模型来增强 scikit-learn。它支持零样本和少样本学习、动态示例选择和可自定义的后端。虽然它提供了灵活性和效率,但用户应该考虑潜在的成本和性能影响,尤其是动态和可调功能。总体而言,Scikit-LLM 是一种强大的工具,可将最先进的语言模型整合到机器学习工作流程中。

更多推荐