5 种文本分类的方法(即使没有训练数据)
介绍
本文重点讨论来自 Flipkart 客户评论数据集的产品评论情感分析。
情感分析是自然语言处理(NLP)中的一项关键任务,旨在将文本分类为积极、消极或中性情感。它使企业能够从客户反馈中获得洞察。
我们的目标是探索和比较多种二元情感分类方法,评估它们的性能和计算效率。
这些方法从传统方法到利用嵌入和生成模型的先进技术不等。
一些方法需要标记数据,而有些方法则完全不需要。
目标
1. 数据集分析与预处理
- 探索 Flipkart 客户评价数据集并对其进行预处理,以创建二元分类任务。
- 关注明确的情感极性(积极和消极),排除中性评价。
2. 方法探索
- 尝试多种文本分类方法,包括:
- a) 使用 TF-IDF 的逻辑回归
- b) 使用 预训练嵌入 的逻辑回归
- 使用嵌入和余弦相似度的 零样本分类。
- 生成语言模型(例如,Flan-T5)。
- 针对评论分类微调的 特定任务情感模型。
3. 性能评估
- 使用标准分类指标评估模型,如 precision、recall、F1-score 和 accuracy。
- 分析性能与计算效率之间的权衡。
为什么情感分析?
情感分析使企业能够:
- 理解客户的意见和偏好。
- 识别产品反馈中的趋势和问题。
- 增强产品改进和客户满意度的决策能力。
该项目利用广泛的自然语言处理技术,展示如何将各种方法应用于情感分析,每种方法都针对特定的用例和资源限制进行调整。
方法概述
1. 使用 TF-IDF 的逻辑回归
- 一种简单而有效的基线,使用基于词频的特征进行分类。
2. 带有预训练嵌入的逻辑回归
- 利用高级嵌入模型,如
all-MiniLM-L6-v2,生成用于训练分类器的语义表示。
3. 零样本分类
- 通过利用文档和标签嵌入之间的余弦相似度,执行无需标记数据的分类。
4. 生成模型
- 探索生成语言模型,如
Flan-T5,它通过根据提示生成响应来对文本进行分类。
5. 任务特定情感模型
- 利用微调的情感模型,如
juliensimon/reviews-sentiment-analysis,以实现领域特定的性能。
环境准备
在开始之前,请确保已安装所有必要的依赖项。
!pip install datasets transformers sentence-transformers pandas scikit-learn tqdm
导入必要的库:
import pandas as pd
import numpy as np
from tqdm import tqdm
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
数据集概述
Flipkart 客户评论数据集由两个主要列组成:
review: 客户关于产品提供的文本评论。
rating: 客户给出的数字评分(星级),范围从 1 到 5。
rating 列表示客户为产品分配的星级数量。较高的值表示更有利的评论。
标签创建
为了简化我们的分析,我们将根据 rating 创建一个新列 label:
- 积极:如果
rating >= 4(例如,4 星或 5 星)
- 消极:如果
rating < 4(例如,1 或 2 星)
- 中立评分(3 星) 将被排除在此分析之外。
这个预处理步骤将帮助我们专注于评论中的明确情感极性(积极和消极)。
data = pd.read_csv('data.csv')
data = data[data["rating"]!=3]
data["label"] = data["rating"].apply(lambda x: 1 if x >= 4 else 0)
print(data["label"].value_counts())
平衡数据集
为了这个例子的目的,我们将对正类进行下采样,以匹配负类的大小。
这确保了数据集的平衡,避免了由于类别不平衡导致的模型训练偏差。
这也确保了更快的计算时间,因为我们处理的数据量更少。
data = pd.concat([
data[data["label"] == 1].sample(n=len(data[data["label"] == 0]),
random_state=1),
data[data["label"] == 0]
])
data = data.sample(frac=1, random_state=1).reset_index(drop=True)
数据划分
我们将数据分为 80% 的训练集和 20% 的测试集。
train = data[:int(0.8*len(data))]
test = data[int(0.8*len(data)):].reset_index(drop=True)
文本分类实验
1. 使用 tf-idf 的逻辑回归
首先,在尝试任何复杂的方法之前,我们将为我们的二分类任务采用简单的逻辑回归。
为了将文本数据以数字形式表示,我们将使用 TF-IDF (词频 - 逆文档频率),这是一种流行的文本数据特征提取技术。
TF-IDF 衡量一个术语在文档中的重要性,相对于整个语料库,提供加权表示,增强独特和信息丰富的单词的影响,同时减少常见单词的权重。
为什么要从逻辑回归和 TF-IDF 开始?
-
简单性和可解释性:
- 逻辑回归简单易懂,易于解释,并提供了特征与标签之间关系的清晰洞察。
- TF-IDF 提供了一种简单而强大的方式,通过强调重要术语并降低常见术语的权重,将文本以数字形式表示。
-
基准性能:
- 这种组合有助于建立一个稳固的基准,以评估未来改进的有效性,例如嵌入或 LLM。
-
效率:
- 逻辑回归和 TF-IDF 都是计算上轻量级的,非常适合较小的数据集和快速实验。
-
经过验证的有效性:
- 逻辑回归与 TF-IDF 配对在文本分类任务中表现良好,尤其是对于具有线性可分特征的数据集。
-
迭代工作流程:
- 从简单开始使我们能够高效地调试和优化管道,然后再引入嵌入或 LLM 等复杂性。
X_train = train['review']
y_train = train['label']
X_test = test['review']
y_test = test['label']
tfidf_vectorizer = TfidfVectorizer()
X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)
X_test_tfidf = tfidf_vectorizer.transform(X_test)
model = LogisticRegression(random_state=1)
model.fit(X_train_tfidf, y_train)
y_pred = model.predict(X_test_tfidf)
print("Classification Report:")
print(classification_report(y_test, y_pred))
2. 使用嵌入作为特征的逻辑回归
在本实验中,我们将利用预训练的嵌入模型进行特征提取,并使用这些嵌入来训练一个轻量级分类器进行二分类。
这种两步法使我们能够利用先进嵌入模型的强大功能,而无需进行微调,从而提高计算效率并使其更易于访问。
步骤:
-
特征提取:
- 使用预训练的嵌入模型(例如,
sentence-transformers/all-MiniLM-L6-v2)将文本输入转换为数值嵌入。
- 嵌入模型保持冻结状态,以确保训练过程中计算成本最小。
-
训练分类器:
- 利用生成的嵌入作为输入特征。
- 在这些特征和标签上训练逻辑回归模型以进行分类。
-
评估:
- 在未见过的数据上测试训练好的分类器。
- 使用精确度、召回率、F1 分数和准确率等指标评估性能。
这种方法的好处:
- 计算效率:嵌入生成可以通过 GPU 加速或外包给 API,而分类器可以在 CPU 上进行训练。
- 简单性:将特征提取和分类分开使得流程简单明了且模块化。
- 多样性:分类器可以轻松替换为其他算法以进行实验。
为什么使用 all-MiniLM-L6-v2?
sentence-transformers/all-MiniLM-L6-v2 模型是一个高效且多功能的嵌入模型,平衡了性能和计算要求,使其成为许多文本处理任务的理想选择。
- 轻量级架构:MiniLM 是基于变换器模型的精简版本,显著减小了模型的体积,同时保持良好的性能。只有 6 层,相比于
all-mpnet-base-v2 等更大模型,它更小更快。
- 高效性:使用
all-MiniLM-L6-v2 生成嵌入非常高效,即使在 CPU 上也能快速处理文本,适合需要实时或大规模嵌入计算的任务。
- 竞争性性能:尽管体积小,但它提供的句子嵌入质量高,适用于许多自然语言处理任务,如分类、聚类和语义搜索。
- 紧凑的嵌入:生成的嵌入具有 384 维,比
all-mpnet-base-v2 等模型的 768 维更紧凑。这些较小的嵌入减少了内存需求,并提高了下游任务的速度。
- 通用性:在广泛的数据集上进行预训练,该模型捕获了通用的语义信息,并且在不同领域中表现良好,无需微调。
我们将首先使用 all-mpnet-base-v2 来比较其与 all-MiniLM-L6-v2 的性能和速度。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('sentence-transformers/all-mpnet-base-v2')
train_embeddings = model.encode(train['review'], show_progress_bar=True)
test_embeddings = model.encode(test['review'], show_progress_bar=True)
clf = LogisticRegression(random_state=1)
clf.fit(train_embeddings, y_train)
y_pred = clf.predict(test_embeddings)
print("Classification Report (MPNet):")
print(classification_report(y_test, y_pred))
我们在大约 7 分钟内获得了 0.86 的准确率。现在我们将使用 all-MiniLM-L6-v2。
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
train_embeddings = model.encode(train['review'], show_progress_bar=True)
test_embeddings = model.encode(test['review'], show_progress_bar=True)
clf = LogisticRegression(random_state=1)
clf.fit(train_embeddings, y_train)
y_pred = clf.predict(test_embeddings)
print("Classification Report (MiniLM):")
print(classification_report(y_test, y_pred))
我们在大约 1 分钟内获得了 0.86 的 F1 分数。速度更快,且没有损失性能。
3. 零样本分类与嵌入
在本实验中,我们将探讨零样本分类,这是一种允许我们在没有任何标记训练数据的情况下执行分类任务的技术。
该方法利用嵌入和余弦相似度将输入文本与预定义的标签描述进行匹配,从而实现无需资源密集型标记工作的分类。
关键步骤:
-
定义标签:为标签创建描述,例如:
- '负面评价'
- '正面评价'
这些描述充当我们的'伪标签',并使用预训练的嵌入模型进行嵌入。
-
嵌入文档和标签:使用预训练模型(例如,sentence-transformers)生成以下内容的嵌入:
-
余弦相似度:
- 使用余弦相似度计算文档嵌入和标签嵌入之间的相似性。
- 将相似度最高的标签分配给每个文档。
-
评估:
- 将预测标签与真实标签进行评估,以评估这种零样本方法的性能。
零样本分类的优势:
- 无需标记数据:消除了昂贵且耗时的手动标记的需求。
- 灵活的标签描述:标签描述可以根据任务或领域进行定制,提高嵌入的相关性。
- 嵌入的多功能性:展示了嵌入在超越监督分类的广泛语言任务中的强大能力。
model = SentenceTransformer('sentence-transformers/all-mpnet-base-v2')
label_descriptions = ["A negative review", "A positive review"]
label_embeddings = model.encode(label_descriptions)
test_embeddings = model.encode(test['review'], show_progress_bar=True)
from sklearn.metrics.pairwise import cosine_similarity
similarity_matrix = cosine_similarity(test_embeddings, label_embeddings)
y_pred = np.argmax(similarity_matrix, axis=1)
print("Classification Report (Zero-Shot):")
print(classification_report(y_test, y_pred))
我们在没有训练数据的情况下获得了令人印象深刻的 0.78 准确率!
我们使用了 all-mpnet-base-v2,而 all-MiniLM-L6-v2 仅获得了 0.66 的准确率,在处理负面评论时遇到了困难。
4. 使用生成模型进行分类
在本实验中,我们将探索如何使用生成语言模型进行文本分类。
与传统的预测数值的分类模型不同,生成模型如Flan-T5生成文本响应,我们将其映射到数值以进行评估。
关键步骤:
-
模型选择:
- 使用 Flan-T5 模型,这是一个序列到序列(编码器 - 解码器)生成模型,通过
text2text-generation 任务加载。
- 从最小的模型
flan-t5-small 开始,以便更快地进行实验,并根据需要扩展到更大的版本(例如,base、large、xl、xxl)。
-
提示工程:
-
数据准备:更新数据集以包括前缀文本:
- 将提示和文本映射到一个新特征,例如
"t5"。
- 这为生成分类管道准备了数据集。
-
推断:
- 在测试数据集上使用管道运行 Flan-T5 模型。
- 收集生成的输出(例如,
"positive" 或 "negative"),并将其转换为数值标签(例如,正面为 1,负面为 0)。
-
评估:
- 使用标准指标(如 F1-score、精确度和召回率)评估模型性能。
- 将文本输出映射到数值,以便与真实值进行比较。
为什么使用 Flan-T5?
-
指令调优的生成能力:
- Flan-T5 系列模型经过超过一千个任务的微调,擅长遵循指令并生成准确的任务特定输出。
-
灵活性:
- 该模型不受预定义类别的限制,可以通过提示工程适应各种任务。
-
预训练的多样性:
- 其生成特性使其能够处理未明确训练过的任务,非常适合任务特定数据较少的场景。
from datasets import Dataset
from transformers import pipeline
from transformers.pipelines.pt_utils import KeyDataset
prompt = "Is the following review positive or negative? "
test['t5'] = prompt + test['review']
dataset = Dataset.from_pandas(test)
pipe = pipeline(
task="text2text-generation",
model="google/flan-t5-small",
device=0
)
y_pred = []
for output in tqdm(pipe(KeyDataset(dataset, key="t5"))):
generated_text = output[0]["generated_text"]
y_pred.append(0 if generated_text == "negative" else 1)
print("Classification Report (Flan-T5):")
print(classification_report(y_test, y_pred))
5. 使用任务特定模型进行情感分析
在这个实验中,我们将使用一个任务特定表示模型进行情感分类。
具体来说,我们将利用预训练的 juliensimon/reviews-sentiment-analysis 模型,该模型经过微调,专门用于对产品评论进行情感分析,以将产品评论分类为正面或负面。
关键步骤:
-
模型加载:
- 使用
transformers.pipeline 加载预训练模型及其分词器。
- 分词器将输入文本拆分为模型可以处理的标记。
-
推理:
- 将测试数据传递给管道,并检索情感分数(例如,负面和正面)。
-
评估:
- 将预测标签(y_pred)与真实标签(y_true)进行比较。使用分类报告评估精确度、召回率、F1 分数和准确率。
model_path = "juliensimon/reviews-sentiment-analysis"
pipe = pipeline(
model=model_path,
tokenizer=model_path,
device=0
)
y_pred = []
for output in tqdm(pipe(KeyDataset(dataset, key="review"))):
label = output["label"]
y_pred.append(0 if "0" in label else 1)
print("Classification Report (Task-Specific):")
print(classification_report(y_test, y_pred))
结果对比与分析
本项目突出了每种方法的优缺点,为将情感分析技术应用于实际数据集提供了全面的指南。
| 方法 | 准确率 | 训练时间 | 推理速度 | 数据需求 |
|---|
| TF-IDF + LR | ~0.80 | 极快 | 极快 | 有标签 |
| Embedding + LR | ~0.86 | 中等 | 快 | 有标签 |
| Zero-Shot | ~0.78 | 无 | 中等 | 无标签 |
| Flan-T5 | ~0.85 | 慢 | 慢 | 有标签/无标签 |
| Task-Specific | ~0.90+ | 无 | 快 | 有标签 |
结论与建议
- 资源受限场景:如果计算资源有限且需要快速部署,TF-IDF + 逻辑回归是最佳起点。它在大多数情况下能提供合理的基线性能。
- 精度优先场景:如果需要更高的准确性且拥有标注数据,预训练嵌入 + 逻辑回归或任务特定模型是更好的选择。它们能捕捉更深层的语义信息。
- 无标签数据场景:当缺乏标注数据时,零样本分类提供了一种可行的替代方案,虽然精度略低,但节省了标注成本。
- 灵活性与扩展性:生成模型(如 Flan-T5)提供了最大的灵活性,可以处理复杂的指令,但推理成本较高,适合对延迟不敏感的场景。
在实际应用中,建议先建立 TF-IDF 基线,然后根据业务需求(精度 vs 速度 vs 成本)逐步迁移到更复杂的模型。