一首(由史上最具标志性的歌手之一演绎的)关于宇宙的歌曲这样唱道:
愿能重回过去
改变这些岁月
正经历着转变
黑色安息日 – 蜕变
这首歌力量十足,描述了生活如何瞬息万变。它讲述了一个心碎的爱情故事,但也令人联想到数据科学家这一职业在过去十年间所经历的巨大变革:
- 在物理学研究初期,当人们提及“Transformer”时,通常只会想到擎天柱。而彼时,机器学习主要指代线性回归、支持向量机(SVM)、随机森林等传统技术。[2016年]
- 在攻读大数据与复杂系统物理学硕士学位期间,“BERT”及各类当时看来极具前景的深度学习技术首次进入人们的视野。首批GPT模型也于同期问世,其当时已显得颇具吸引力,尽管无人预料到它们能发展至今的强大程度。[2018-2020年]
- 时至今日,作为一名全职数据科学家,若不了解GPT的含义,或未曾研读《Attention Is All You Need》这篇开创性论文,通过数据科学系统设计面试的几率将微乎其微。[2021年至今]
当人们提及如今数据工作者的工具和日常工作与十年前(甚至五年前)相比已截然不同时,这一观点无疑是准确的。然而,仅仅因为当前许多问题似乎都能通过GPT、大型语言模型(LLMs)或智能体AI解决,就抛弃过往工具的观念,是值得商榷的。
本文旨在探讨一项具体任务:对推文的情感倾向(爱、恨、中立)进行分类。将分别采用传统机器学习、深度学习和大型语言模型三种方法来完成此任务。
文章将通过Python进行实践演示,并详细阐述每种方法的适用场景及原因。期望读者通过本文能理解并掌握以下几点:
- 早期使用的工具依然值得考虑、研究,并适时采纳。
- 在为特定用例选择最佳算法时,应综合评估延迟、准确性和成本。
- 数据科学家领域的变革是必然的,应无所畏惧地积极拥抱

现在,让我们开始吧!
1. 案例分析
本文所处理的案例在数据科学和人工智能应用中极为常见:情感分析。这意味着,给定一段文本,需要从中推断出作者所表达的“情感”。这对于收集某个商品、电影、推荐项目等评价反馈的场景非常有用。
在这篇博文中,将使用一个“著名”的情感分析示例,即对推文背后情感进行分类。为了更好地控制实验条件,本文不采用从网络抓取的真实推文(其标签可能存在不确定性),而是使用通过大型语言模型生成且可控的内容。
这种技术还允许调整问题的难度和多样性,从而观察不同技术对此类挑战的反应。
-
简单案例:积极推文如同明信片般充满爱意,消极推文直白强烈,中立信息则围绕天气和咖啡等日常话题。若模型在此类简单任务中表现不佳,则说明存在其他问题。
-
复杂案例:依然包含爱、恨、中立情感,但其中注入了讽刺、混合语调和需要关注上下文的微妙暗示。此外,用于训练的数据量也更少,以构建一个更小的数据集进行训练。
-
极难案例:情感类别增加至五种:爱、恨、愤怒、厌恶、嫉妒,模型需要解析更丰富、更具层次感的句子。更具挑战的是,训练数据为零,这意味着无法进行任何模型训练。
目标是构建一个能够有效捕捉推文背后情感的智能分类系统。那么,如何实现这一目标呢?让我们一探究竟。
2. 系统设计
下图展示了一个在系统设计中始终极具参考价值的考量要素:

在机器学习系统中,准确性、成本和规模构成一个三元悖论。通常只能同时充分优化其中两者。
可以构建一个在处理数百万条数据时表现出高准确性和出色可扩展性的模型,但其响应速度可能不够快。也可以拥有一个快速且能处理海量数据的模型,但其准确性可能有所牺牲。同样,一个既准确又快速的模型,其可扩展性可能不尽理想。
这些考量虽抽象于具体问题,但能为机器学习系统设计提供指导。稍后将再次探讨这一点。
此外,模型的性能应与训练集的大小相称。通常情况下,应避免以测试集误差增加为代价来降低训练集误差(即著名的过拟合问题)。

模型不应处于欠拟合或过拟合的区域。其原因如下:
简单来说,欠拟合是指模型过于简单,无法学习数据中的真实模式,就像试图用一条直线来拟合螺旋线。过拟合则恰恰相反,模型对训练数据学习得过于透彻,甚至包括了其中的噪声,导致在已见过的数据上表现出色,但在新数据上表现糟糕。最佳状态是处于两者之间的“甜蜜点”,即模型理解数据结构而并非死记硬背。
稍后也将再次探讨这一点。
3. 简单案例:传统机器学习
文章将从最友好的场景开始探讨:一个包含1000条推文的结构化数据集,这些推文已生成并标注。为确保平衡,三种类别(积极、中立、消极)被刻意设定为均衡分布,语言表达也极为明确,每行数据均整齐地保存在CSV文件中。
首先,导入必要的代码库。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
from future import annotations
from pathlib import Path
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.decomposition import TruncatedSVD
from sklearn.featureextraction.text import TfidfVectorizer
from sklearn.linearmodel import LogisticRegression
from sklearn.metrics import ConfusionMatrixDisplay, classificationreport, confusionmatrix
from sklearn.modelselection import traintest_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
sns.settheme(style=”whitegrid”)
RANDOMSTATE=42
DATA_PATH=Path(“data/tweets.csv”)
接下来,查看数据集的结构:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
if not DATAPATH.exists():
raise FileNotFoundError(f”Dataset not found at {DATAPATH.resolve()}”)
df=pd.readcsv(DATAPATH)
df=df.dropna(subset=[“label”, “tweet”]).copy()
df.head()
| | label | tweet |
| — | — | — |
| 0 | neutral | 全新的城市发展规划旨在平衡绿地与住宅区,以适应不断增长的城市人口,同时维护环境可持续性。 |
| 1 | neutral | 最新数据显示,全球可再生能源使用量持续增长,反映出多个行业对太阳能、风能及其他可持续能源投资的日益增加。 |
| 2 | neutral | 研究表明,规律的体育活动显著有助于改善心理健康,强调了将锻炼融入日常生活以促进整体福祉的重要性。 |
| 3 | neutral | 教育成果报告凸显了不同地区之间存在差异的学业成就水平,呼吁采取有针对性的干预措施以满足多样化的学习需求。 |
| 4 | neutral | 气候变化影响研究强调了气温上升和降水模式改变,这对全球农业和自然生态系统构成了复杂挑战。 |

可以预见,对于数百万行数据而言,这种方法难以扩展(因为数据集结构过于规整,多样性不足)。然而,对于这个微小而特定的用例,可以构建一个非常快速且准确的方法。接下来开始建模。主要考虑以下三点:
- 将数据集的20%划分为测试集进行训练/测试分割。
- 将采用TF-IDF方法来获取词语的嵌入表示。TF-IDF,即词频-逆文档频率,是一种经典的技术,通过衡量词语在文档中的重要性及其在整个数据集中的稀有程度来将文本转换为数值。
- 该技术将与两种机器学习模型结合使用:来自scikit-learn的逻辑回归和支持向量机。逻辑回归模型简单易懂,常作为文本分类的强大基线。支持向量机则专注于寻找类别间的最佳决策边界,通常在数据噪声不大时表现出色。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
X=df[“tweet”].astype(str)
y=df[“label”].astype(str)
Xtrain, Xval, ytrain, yval=traintestsplit(
X,
y,
testsize=0.2,
randomstate=RANDOM_STATE,
stratify=y,
)
def makepipeline(model):
return Pipeline([
(“tfidf”, TfidfVectorizer(
lowercase=True,
stripaccents=”unicode”,
ngramrange=(1, 2),
mindf=2,
max_df=0.95,
)),
(“clf”, model),
])
pipelines= {
“LogReg”: makepipeline(LogisticRegression(
C=2.0,
classweight=”balanced”,
maxiter=2000,
solver=”liblinear”,
randomstate=RANDOMSTATE,
)),
“LinearSVC”: makepipeline(LinearSVC(
C=1.0,
classweight=”balanced”,
randomstate=RANDOM_STATE,
)),
}
results= []
predictions= {}
for name, pipe in pipelines.items():
model=pipe.fit(Xtrain, ytrain)
ypred=model.predict(Xval)
predictions[name] =y_pred
report=classificationreport(yval, ypred, outputdict=True)
results.append({
“model”: name,
“accuracy”: report[“accuracy”],
“f1macro”: report[“macro avg”][“f1-score”],
“precisionmacro”: report[“macro avg”][“precision”],
“recall_macro”: report[“macro avg”][“recall”],
})
resultsdf=pd.DataFrame(results).sortvalues(by=”f1macro”, ascending=False)
resultsdf
| | model | accuracy | f1macro | precisionmacro | recall_macro |
| — | — | — | — | — | — |
| 0 | LogReg | 0.98 | 0.979975 | 0.980605 | 0.979873 |
| 1 | LinearSVC | 0.98 | 0.979975 | 0.980605 | 0.979873 |
两种模型的性能均接近完美。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
bestmodelname=resultsdf.iloc[0][“model”]
ypredbest=predictions[bestmodelname]
cm=confusionmatrix(yval, ypred_best, labels=sorted(y.unique()))
fig, ax=plt.subplots(figsize=(5, 4))
disp=ConfusionMatrixDisplay(confusionmatrix=cm, displaylabels=sorted(y.unique()))
disp.plot(ax=ax, cmap=”Blues”, colorbar=False)
ax.grid(False)
ax.settitle(f”{bestmodel_name} confusion matrix (validation)”)
plt.show()

对于这种具有1000行一致数据的简单案例,传统的机器学习方法足以胜任。无需动用像GPT这样拥有数十亿参数的大型模型。
4. 复杂案例:深度学习
第二个数据集依然是合成数据,但被刻意设计成更具挑战性。情感标签仍为爱、恨、中立,但推文内容则倾向于讽刺、混合语调和含蓄的赞美。此外,训练集规模较小,而验证集保持较大,这使得模型在更少证据和更多模糊性下工作。
| id | label | tweet |
| — | — | — |
| 6 | neutral | 此事偶有吸引之处,但也并非毫无波折。算不上出类拔萃,但也不至于完全被遗忘。 |
| 43 | positive | 虽遇挫折,步履维艰,却令最终的胜利更显甜美。有时,磨砺亦是值得。 |
| 93 | negative | 进步的表象诱人,但深入探究,便会发现旧疾依然如故。 |
| 40 | positive | 诚然,早晨开局不顺,但事实证明,那些小插曲反而为后续的重大进展铺垫。对未来充满斗志!
|
| 38 | positive | 并非所有环节都尽善尽美,但这并未妨碍美好事物的脱颖而出。有时,不完美反能增添恰到好处的风味。 |
面对这种模糊性,需要动用更强大的工具。深度学习嵌入模型在这些情况下能保持较高的准确性并具有良好的可扩展性(回想一下之前的“准确性、成本、规模”三元悖论和“误差与复杂度”曲线!)。具体而言,深度学习嵌入模型通过上下文而非孤立的词符来学习词语的含义。
本文将使用BERT,这是目前最著名的嵌入模型之一。首先,导入一些库:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
from future import annotations
from pathlib import Path
from typing import Dict, Tuple
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import torch
from transformers import AutoModel, AutoTokenizer
from sklearn.decomposition import TruncatedSVD
from sklearn.featureextraction.text import TfidfVectorizer
from sklearn.linearmodel import LogisticRegression
from sklearn.metrics import accuracyscore, classificationreport, confusionmatrix, f1score
from sklearn.modelselection import traintest_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoder
sns.settheme(style=”whitegrid”)
RANDOMSTATE=42
DEVICE=torch.device(“cuda”if torch.cuda.isavailable() else”cpu”)
MODELNAME=”distilbert-base-uncased”
……以及一些辅助函数。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
def loaddataset(path: Path) ->pd.DataFrame:
if not path.exists():
raise FileNotFoundError(f”Dataset missing: {path.resolve()}”)
df=pd.readcsv(path).dropna(subset=[“label”, “tweet”]).copy()
df[“tweet”] =df[“tweet”].astype(str)
df[“label”] =df[“label”].astype(str)
return df
def stratifiedsplit(df: pd.DataFrame, testsize: float=0.7):
X=df[“tweet”].values
y=df[“label”].values
return traintestsplit(
X,
y,
testsize=testsize,
randomstate=RANDOMSTATE,
stratify=y,
)
def evaluatetfidf(df: pd.DataFrame) ->Dict[str, float]:
Xtrain, Xval, ytrain, yval=stratifiedsplit(df)
pipeline=Pipeline([
(“tfidf”, TfidfVectorizer(
lowercase=True,
stripaccents=”unicode”,
ngramrange=(1, 2),
mindf=2,
max_df=0.95,
)),
(“clf”, LogisticRegression(
C=2.0,
classweight=”balanced”,
maxiter=2000,
solver=”liblinear”,
randomstate=RANDOMSTATE,
)),
])
model=pipeline.fit(Xtrain, ytrain)
ypred=model.predict(Xval)
report=classificationreport(yval, ypred, outputdict=True)
metrics= {
“accuracy”: report[“accuracy”],
“f1macro”: report[“macro avg”][“f1-score”],
“precisionmacro”: report[“macro avg”][“precision”],
“recall_macro”: report[“macro avg”][“recall”],
}
return metrics
def encodewithbert(texts: np.ndarray, batchsize: int=32) ->np.ndarray:
embeddings= []
for start in range(0, len(texts), batchsize):
batch=list(texts[start:start+batchsize])
inputs=tokenizer(
batch,
padding=True,
truncation=True,
maxlength=128,
returntensors=”pt”,
).to(DEVICE)
with torch.nograd():
outputs=bertmodel(**inputs)
clsembeddings=outputs.lasthiddenstate[:, 0, :]
embeddings.append(cls_embeddings.cpu().numpy())
return np.vstack(embeddings)
def evaluatebertembeddings(df: pd.DataFrame) ->Dict[str, float]:
Xtrain, Xval, ytrain, yval=stratified_split(df)
Xtrainemb=encodewithbert(Xtrain)
Xvalemb=encodewithbert(Xval)
clf=LogisticRegression(
classweight=”balanced”,
maxiter=1000,
multiclass=”auto”,
solver=”lbfgs”,
randomstate=RANDOMSTATE,
)
clf.fit(Xtrainemb, ytrain)
ypred=clf.predict(Xval_emb)
report=classificationreport(yval, ypred, outputdict=True)
metrics= {
“accuracy”: report[“accuracy”],
“f1macro”: report[“macro avg”][“f1-score”],
“precisionmacro”: report[“macro avg”][“precision”],
“recall_macro”: report[“macro avg”][“recall”],
}
return metrics
得益于这些辅助函数,可以快速评估嵌入模型与TF-IDF方法的性能差异。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
tokenizer=AutoTokenizer.frompretrained(MODELNAME)
bertmodel=AutoModel.frompretrained(MODELNAME).to(DEVICE)
bertmodel.eval()
datasets= {
“clean”: loaddataset(Path(“data/tweets.csv”)),
“hard”: loaddataset(Path(“data/tweets_hard.csv”)),
}
metricsrows= []
for name, df in datasets.items():
tfidfmetrics=evaluatetfidf(df)
bertmetrics=evaluatebertembeddings(df)
metricsrows.append({“dataset”: name, “model”: “tfidflogreg”, **tfidfmetrics})
metricsrows.append({“dataset”: name, “model”: “bertlogreg”, **bertmetrics})
metricsdf=pd.DataFrame(metrics_rows)
metrics_df
| | dataset | model | accuracy | f1macro | precisionmacro | recallmacro |
| — | — | — | — | — | — | — |
| 0 | clean | tfidflogreg | 0.99 | 0.990021 | 0.990201 | 0.989992 |
| 1 | clean | bertlogreg | 0.995714 | 0.995723 | 0.995781 | 0.995708 |
| 2 | hard | tfidflogreg | 0.685714 | 0.67517 | 0.72303 | 0.684179 |
| 3 | hard | bert_logreg | 0.9 | 0.901723 | 0.907247 | 0.899758 |


如结果所示,TF-IDF模型在处理积极标签时表现显著不足,而采用嵌入模型(BERT)则能保持较高的准确性。
5. 极难案例:大型语言模型(LLM)智能体
接下来,将挑战一个极具难度的问题:
- 仅有100条数据。
- 假定标签未知,这意味着无法训练任何机器学习模型。
- 拥有五种标签:嫉妒、憎恨、爱、厌恶、愤怒。
| id | label | tweet |
| — | — | — |
| 99 | envy | 每当你分享你的成就,那都是一种苦乐参半的提醒,告诉我离成功如此之近,却又感到你那边风景如此遥远。 |
| 11 | love | 你的善良是一首在我灵魂中轻柔奏响的旋律,让平凡的日子也变得非凡。 |
| 21 | hate | 有些日子,无用的闲聊和虚假的笑容让我只想对着虚空尖叫,直到能有一刻的寂静——哪怕只有一刻。 |
| 62 | disgust | 刷过那个画面时,感觉眼睛都在灼烧。人们怎么能忍受那堆烂摊子而不作呕?恶心都无法形容。 |
| 1 | love | 你的每一个小习惯都让我爱得更深,就像你以为没人看时那轻柔的笑声。 |

由于无法进行任何模型训练,但仍希望执行分类任务,因此必须采用某种内含分类知识的方法。大型语言模型正是此类方法的最佳范例。
值得注意的是,若在前两种情况下使用大型语言模型,无异于杀鸡用牛刀。然而在此情境下,它却恰如其分:任务复杂且缺乏训练数据,使得无法采用传统智能方法进行模型训练。这正是大型语言模型发挥作用的理想场景。
在这种情况下,模型能在大规模数据上保持高准确性。然而,API响应需要一定时间,因此在获得结果之前可能需要等待一到两秒(回想一下之前的“三元悖论”!)。
现在,导入一些必要的库:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
import json
from pathlib import Path
from typing import Dict, List
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import accuracyscore, classificationreport, confusion_matrix
from openai import OpenAI
sns.set_theme(style=”whitegrid”)
DATAPATH=Path(“data/tweetshardmanyclasses.csv”)
OUTPUTJSON=Path(“data/tweetsllmpredictions.json”)
CHUNKSIZE=10
MODELNAME=”gpt-5″
EMOTIONS= [“love”, “hate”, “anger”, “disgust”, “envy”]
apikey=”yourapi_key”
client=OpenAI(apikey=apikey)
以下是分类API的调用代码:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
SYSTEM_PROMPT= (
“You are an analytical assistant. ”
“Given a short tweet, classify it into one of five emotions: love, hate, anger, disgust, envy. ”
“Respond in JSON only. Provide both the predicted label and a short interpretation explaining why.”
)
def builduserprompt(batch: pd.DataFrame) ->str:
lines= [
“Classify each tweet. Return JSON with fields: number (int), label (string), interpretation (string).”,
]
for idx, row in batch.iterrows():
lines.append(f”Tweet {idx}: {row[‘tweet’]}”)
return”
“.join(lines)
def parsepredictions(rawtext: str) ->List[Dict]:
cleaned=raw_text.strip()
if cleaned.startswith(“") and cleaned.endswith("“):
cleaned=cleaned.strip(“`”).strip()
if cleaned.startswith(“json”):
cleaned=cleaned[4:].strip()
if”}
{“in cleaned:
parts=cleaned.split(“}
{“)
cleaned=”{“.join([parts[0]] +parts[1:])
payload=json.loads(cleaned)
if isinstance(payload, dict) and”predictions”in payload:
return payload[“predictions”]
if isinstance(payload, list):
return payload
raise ValueError(“Unexpected JSON structure from model”)
def classifywithllm(df: pd.DataFrame) ->List[Dict]:
results: List[Dict] = []
for start in range(0, len(df), CHUNKSIZE):
batch=df.iloc[start:start+CHUNKSIZE]
prompt=builduserprompt(batch)
response=client.responses.create(
model=MODELNAME,
input=[
{“role”: “system”, “content”: SYSTEMPROMPT},
{“role”: “user”, “content”: prompt}
],
)
predictions=parsepredictions(response.outputtext)
for pred in predictions:
number=pred.get(“number”)
if number not in batch.index:
continue
label=pred.get(“label”, “”).strip().lower()
if label not in EMOTIONS:
continue
interpretation=pred.get(“interpretation”, “”).strip()
results.append({
“index”: number,
“predicted_label”: label,
“interpretation”: interpretation,
})
return results
if OUTPUTJSON.exists():
with open(OUTPUTJSON, “r”, encoding=”utf-8″) as f:
predictions=json.load(f)
print(f”Loaded cached predictions from {OUTPUTJSON}”)
else:
predictions=classifywithllm(df)
with open(OUTPUTJSON, “w”, encoding=”utf-8″) as f:
json.dump(predictions, f, indent=2, ensureascii=False)
print(f”Wrote predictions to {OUTPUTJSON}”)
结果表明,大型语言模型在分类任务中表现出色:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
preddf=pd.DataFrame(predictions).setindex(“index”).sortindex()
fulldf=df.join(pred_df, how=”left”)
| | label | tweet | predicted_label | interpretation |
| — | — | — | — | — |
| 72 | disgust | 那个地方给人的感觉深感不安,仿佛浸透了疏忽与腐朽。 | disgust | 描述了令人不安和排斥的忽视与腐朽感。 |
| 89 | envy | 他们眼中闪烁的光芒,源于他们身处理想之地——而我却在此处,渴望一瞥那 elusive 的成功。 | envy | 渴望他人所拥有的成功,捕捉到嫉妒的情绪。 |
| 96 | envy | 你身上散发的光芒,仿佛掌握着美好生活的秘密。而我却困惑不解,那种感觉究竟如何。 | envy | 对他人看似幸福的经历充满好奇和渴望。 |
| 51 | anger | 我受够了无休止的借口和相互指责。清醒点——你的疏忽正在让所有人付出代价,除了我们其他人,没人会收拾这个烂摊子! | anger | 直接斥责疏忽和借口,显示出强烈的沮丧和指责。 |
| 19 | love | 看着你睁大眼睛梦想,让我相信有一种爱,能让星辰也为之嫉妒。 | love | 对他人的梦想充满浪漫的灵感和惊叹,明确表达了爱意。 |
| 22 | hate | 看着人们为了爬上油腻的阶梯而踩踏他人,我心中燃起无尽的怒火。同理心显然早已消逝。 | anger | 对他人剥削他人以求上位的行为表示极度愤怒。 |
| 5 | love | 每当我看到那个笑容,世界仿佛为之一滞,只为庆祝那份真诚与善良的美好。 | love | 崇拜他人的笑容和善良,赞美其真实的魅力。 |
| 68 | disgust | 黏腻的残渣和恶臭侵扰着我的思绪。说实话,保持面无表情简直不可能。 | disgust | 黏腻的残渣和恶臭引发强烈的厌恶。 |
| 4 | love | 有时,仅仅是看到你的名字出现在屏幕上,就足以点亮整个星期。你超出了我所有的期望。 | love | 因看到他人的名字而感到喜悦和满足,并对其深感珍视。 |
| 34 | hate | 是否曾有过那种令人沮丧的感觉,当你意识到某人的全部存在都只是噪音?烦人、无情、筋疲力尽——真希望他们消失。 | hate | 对某人的存在感到强烈厌恶,并希望其消失,表明了强烈的憎恨。 |
6. 结论
在过去十年中,数据科学家的角色随着技术的演进发生了翻天覆地的变化。这可能导致人们倾向于直接采用最强大的工具,但这并非所有情况下的最佳途径。
与其盲目追求最庞大的模型,本文通过准确性、延迟和成本这三个简单视角,对一个实际问题进行了深入探讨。
具体而言,本文逐步完成了以下工作:
-
将推文情感分类定义为应用案例,目标是识别爱、恨或中立的情感意图。为此,设计了三个难度递增的数据集:一个清晰数据集、一个包含讽刺意味的数据集,以及一个零训练数据数据集。
-
针对简单案例,采用TF-IDF结合逻辑回归和支持向量机进行处理。 由于推文内容清晰直接,两种模型均表现出近乎完美的性能。
-
随后转向复杂案例,其中讽刺、混合语调和微妙语境增加了任务难度。 此时,采用BERT嵌入来捕捉超越单个词汇的深层语义。
-
最后,在没有训练数据的极难案例中,直接利用大型语言模型通过零样本学习完成了情感分类任务。
每一步都揭示了合适的工具如何取决于具体问题。当数据结构化时,传统机器学习模型快速且可靠。当语义隐藏在字里行间时,深度学习模型能提供帮助。而当缺乏标签或需要广泛泛化能力时,大型语言模型则显得尤为强大。
