📢 转载信息
原文作者:Iván Palomares Carrascosa
在本文中,您将学习如何通过将大型语言模型(LLM)嵌入作为工程特征,来有意义地改进时间序列预测性能。
我们将涵盖的主题包括:
- 构建一个仅使用传统时间序列特征的基线预测模型。
- 从金融新闻标题生成并降维大型语言模型嵌入。
- 比较有嵌入和无嵌入特征的模型性能。
让我们直接开始。
LLM 嵌入能否提升时间序列预测性能?一项实际的特征工程方法(点击放大)
图片由编辑提供
引言
使用大型语言模型(LLMs)——或者说它们产生的输出来解决各种机器学习驱动的任务,包括那些在语言模型出现之前就已经解决过的预测任务,已经成为一种趋势。这自然会引出一个关于时间序列预测的问题:利用 LLMs,例如通过使用 LLM 生成的嵌入作为附加特征,是否真的有助于提高为时间序列预测训练的模型的性能?
本文旨在通过一个鼓励批判性思考的逐步实践示例,来解决关于 LLMs 在预测未来等预测任务中的实用性这一紧迫问题。
使用 LLM 嵌入改进金融时间序列预测:实际演练
我们首先导入本示例所需的模块和库。在本示例中,我们将创建两个版本的用于训练预测模型的数据集:一个仅包含时间序列相关特征,另一个则包含从另一个(但有一定因果关系)数据集中派生的 LLM 生成的嵌入。
使用的两个数据集是:
- 道琼斯工业平均指数:DJIA 中 30 家大型美国公司的每日调整后收盘价。我们将使用 yfinance 库检索此数据集。
Combined_News_DJIA标题:每日排名前 25 位财经新闻标题,大部分包含 DJIA 上市公司。
import pandas as pd
import numpy as np
import yfinance as yf
from sentence_transformers import SentenceTransformer
from sklearn.decomposition import PCA
from sklearn.metrics import accuracy_score
from lightgbm import LGBMClassifier
接下来的代码片段加载第一个数据集,并应用简单的特征工程来添加滞后特征和滚动统计数据——这是时间序列预处理中的常见做法,以更好地捕捉对下游预测任务有意义的信号模式:
ticker = "^DJI"
df_price = yf.download(ticker, start="2008-01-01", end="2016-12-31")
df_price = df_price[["Close"]]
df_price["return"] = df_price["Close"].pct_change()
df_price["target"] = (df_price["return"].shift(-1) > 0).astype(int)
df_price = df_price.dropna()
df_price.head() # Adding lagged and rolling average features for lag in [1, 2, 3, 5]:
for lag in [1, 2, 3, 5]:
df_price[f"lag_{lag}"] = df_price["return"].shift(lag)
df_price["roll_mean_5"] = df_price["return"].rolling(5).mean()
df_price["roll_std_5"] = df_price["return"].rolling(5).std()
df_price = df_price.dropna()
请注意这些工程特征名称中使用的前缀,我们稍后会再次引用它们。
接下来,我们加载新闻标题数据集,并将每日标题合并到一个文本列中。这是一种为了说明目的而故意简化的方法,可以使其更加复杂——例如,可以通过更严格地过滤 DJIA 相关内容的新闻标题。
df_news = pd.read_csv( "https://raw.githubusercontent.com/gakudo-ai/open-datasets/refs/heads/main/Combined_News_DJIA.csv" )
df_news.columns = df_news.columns.str.strip()
df_news["Date"] = pd.to_datetime(df_news["Date"], dayfirst=True)
headline_cols = [c for c in df_news.columns if c.startswith("Top")]
df_news[headline_cols] = df_news[headline_cols].fillna("")
df_news["combined"] = df_news[headline_cols].apply( lambda row: " ".join([str(x) for x in row if str(x).strip() != ""]), axis=1 )
df_news["combined"].head()
以下辅助函数并非严格必需,但对于移除因该数据集中某些字符串最初被字节编码而产生的奇怪字符很有用:
import ast
def clean_bytes_text(x):
"""
Convert byte-like strings (b'...') to normal text
"""
if isinstance(x, str):
try:
evaluated_value = ast.literal_eval(x)
if isinstance(evaluated_value, bytes):
return evaluated_value.decode('utf-8')
else:
return str(evaluated_value)
except (ValueError, SyntaxError):
if x.startswith("b"):
return x.strip("b'\"")
return x
return x
df_news["combined"] = df_news["combined"].apply(clean_bytes_text)
我们现在使用预先训练的句子转换器模型来生成嵌入,将 “combined” 列中的连接新闻标题作为输入:
model = SentenceTransformer("all-MiniLM-L6-v2")
embeddings = model.encode(
df_news["combined"].tolist(),
show_progress_bar=True
)
为了降低过拟合风险并限制嵌入空间的维度,我们应用主成分分析(PCA):
pca = PCA(n_components=20)
embeddings_reduced = pca.fit_transform(embeddings)
emb_df = pd.DataFrame(
embeddings_reduced,
index=df_news["Date"],
columns=[f"emb_{i}" for i in range(20)]
)
现在来到流程中最复杂的部分:将时间序列特征与生成的嵌入维度合并到一个数据集中。该过程包括准备 df_price 进行合并,清理列名,展平潜在的 MultiIndex 列,并确保 'Date' 表示为单层列。然后将结果与 emb_df 合并(基于 'Date')。
emb_df = emb_df.copy()
emb_df.reset_index(inplace=True)
emb_df.rename(columns={emb_df.columns[0]: "Date"}, inplace=True)
if 'level_0' in df_price.columns:
df_price = df_price.drop(columns=['level_0'])
if 'index' in df_price.columns:
df_price = df_price.drop(columns=['index'])
df_price_cleaned = df_price.reset_index()
if isinstance(df_price_cleaned.columns, pd.MultiIndex):
new_cols = []
for col in df_price_cleaned.columns:
if col[0] == 'Date':
new_cols.append('Date')
elif isinstance(col, tuple) and col[1] != '':
new_cols.append(col[1])
else:
new_cols.append(col[0])
df_price_cleaned.columns = new_cols
if 'Ticker' in df_price_cleaned.columns:
df_price_cleaned = df_price_cleaned.drop(columns=['Ticker'])
df_price_cleaned['Date'] = pd.to_datetime(df_price_cleaned['Date'])
df = pd.merge(df_price_cleaned, emb_df, on="Date", how="inner")
回顾一下用于时间序列特征的前缀。现在我们将列名分为两组:一组与传统时间序列属性相关,另一组与 LLM 嵌入对应。
emb_features = [col for col in df.columns if "emb_" in col]
ts_features = [col for col in df.columns if "lag_" in col or "roll_" in col]
有了这些特征...
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区