解决时间序列Transformer股票预测中的张量形状不匹配错误
问题描述
我正在训练TimeSeries Transformer模型,用前一日股价及其他参数预测股票价格变化,期间遇到一系列张量形状、维度相关错误,当前错误如下:
文件"\transformers\models\time_series_transformer\modeling_time_series_transformer.py",第1378行,forward方法中:transformer_inputs, loc, scale, static_feat = self.create_network_inputs(
文件"\transformers\models\time_series_transformer\modeling_time_series_transformer.py",第1303行,create_network_inputs方法中:抛出ValueError: input length 11 and time feature lengths 13 does not match
我的样本输入数据集包含10行、70个特征,搞不懂错误里的11和13是哪来的。之前还遇到过类似错误:
modeling_time_series_transformer.py",第1272行,create_network_inputs方法中:
(torch.cat((past_values, future_values), dim=1) - loc) / scale
RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 2 but got size 1 for tensor number 1 in the list.
RuntimeError: Tensors must have same number of dimensions: got 3 and 2
代码
import pandas as pd import numpy as np from transformers import TimeSeriesTransformerModel, TimeSeriesTransformerConfig, Trainer, TrainingArguments, default_data_collator from sklearn.preprocessing import MinMaxScaler, OneHotEncoder from sklearn.metrics import mean_squared_error import torch from torch.utils.data import Dataset # Load the CSV file file_path = './spy-stock-price - Spy_Ind_Signal.csv' data = pd.read_csv(file_path) # Exclude specified columns exclude_columns = ['20SMA', '50SMA', '200SMA', '20EMA', '10EMA', 'MACD', 'MACD_Signal', 'Average_Volume', 'Bollinger_High', 'Bollinger_Low', 'Bollinger_Middle', 'VWAP', 'AVWAP'] data = data.drop(columns=exclude_columns) # Preprocess the data data['Date'] = pd.to_datetime(data['Date']) data = data.sort_values('Date') # Encode categorical signals data_transformed = data # Drop the 'Date' column data_transformed = data_transformed.drop(columns=['Date']) # Normalize the dataset scaler = MinMaxScaler(feature_range=(0, 1)) scaled_data = scaler.fit_transform(data_transformed) # Convert the data to a supervised learning problem def create_dataset(data, look_back=1): X, Y = [], [] for i in range(len(data) - look_back - 1): a = data[i:(i + look_back)] X.append(a) Y.append(data[i + look_back, -2:]) # Include the last two columns as targets if Y[-1] is None: print(f"NoneType found at index {i + look_back}") return np.array(X), np.array(Y) look_back = 10 # Adjusted look_back to 10 X, y = create_dataset(scaled_data, look_back) # Split into train and test sets train_size = int(len(X) * 0.67) X_train, X_test = X[0:train_size], X[train_size:] y_train, y_test = y[0:train_size], y[train_size:] # Reshape input to be [samples, time steps, features] X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], data_transformed.shape[1])) X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], data_transformed.shape[1])) # Create observed mask for the transformer model def create_observed_mask(data): mask = np.ones_like(data, dtype=np.float32) return mask train_observed_mask = create_observed_mask(X_train) test_observed_mask = create_observed_mask(X_test) # Convert data to PyTorch tensors X_train = torch.tensor(X_train, dtype=torch.float32) X_test = torch.tensor(X_test, dtype=torch.float32) y_train = torch.tensor(y_train, dtype=torch.float32) y_test = torch.tensor(y_test, dtype=torch.float32) train_observed_mask = torch.tensor(train_observed_mask, dtype=torch.float32) test_observed_mask = torch.tensor(test_observed_mask, dtype=torch.float32) # Create a custom dataset class class TimeSeriesDataset(Dataset): def __init__(self, X, y, observed_mask): self.X = X self.y = y self.observed_mask = observed_mask def __len__(self): return len(self.X) def __getitem__(self, idx): time_dim = self.X.shape[1] feature_dim = self.X.shape[2] future_values = self.y[idx].unsqueeze(0).repeat(time_dim, feature_dim // 2) static_categorical_features = torch.tensor([]).unsqueeze(0).repeat(time_dim, feature_dim) static_real_features = torch.zeros((time_dim, feature_dim)) static_feat = torch.zeros((time_dim, feature_dim, 1)) sample = { 'past_values': self.X[idx], 'past_time_features': torch.zeros((time_dim, feature_dim)), # Add this line 'past_observed_mask': self.observed_mask[idx], 'future_values': future_values, 'future_time_features': future_values, } return sample train_dataset = TimeSeriesDataset(X_train, y_train, train_observed_mask) test_dataset = TimeSeriesDataset(X_test, y_test, test_observed_mask) # Model configuration config = TimeSeriesTransformerConfig( prediction_length=1, context_length=look_back, lags_seq=[1, 2, 3], input_size=data_transformed.shape[1], output_size=2, # Predicting both price_change and bull_bear_signal num_time_features=data_transformed.shape[1], # Match the input size num_static_categorical_features=0, num_static_real_features=0, cardinality=[], embedding_dimension=[] ) model = TimeSeriesTransformerModel(config) # Training configuration training_args = TrainingArguments( output_dir="./results", eval_strategy="epoch", learning_rate=1e-4, per_device_train_batch_size=16, per_device_eval_batch_size=16, num_train_epochs=10, weight_decay=0.01, logging_dir="./logs", ) # Training trainer = Trainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=test_dataset, data_collator=default_data_collator, ) trainer.train() # Evaluation predictions, labels, _ = trainer.predict(test_dataset) # Inverse transform the predictions and labels predictions = scaler.inverse_transform(predictions) labels = scaler.inverse_transform(y_test.numpy()) # Separate the predictions and labels for price_change and bull_bear_signal predictions_price_change = predictions[:, 0] predictions_bull_bear_signal = predictions[:, 1] labels_price_change = labels[:, 0] labels_bull_bear_signal = labels[:, 1] # Calculate the Mean Squared Error for price_change mse_price_change = mean_squared_error(labels_price_change, predictions_price_change) print(f"Mean Squared Error for Price Change: {mse_price_change}") # For bull_bear_signal, we can use accuracy as the metric accuracy_bull_bear_signal = np.mean(predictions_bull_bear_signal.round() == labels_bull_bear_signal.round()) print(f"Accuracy for Bull Bear Signal: {accuracy_bull_bear_signal}")
已采取的步骤
- 升级transformers库到4.42版本
- 在HuggingFace和Stack Overflow搜索解决方案,能解决当前错误但会出现同类变体错误
- 修复过多个同类错误,确保past_values、past_time_features、future_values等张量形状一致
问题根源与修复方案
1. 错误数值解析
错误里的input length=11是上下文长度+预测长度(10+1=11),time feature lengths=13是模型自动计算的上下文长度+预测长度+lags序列长度(10+1+2=13),核心问题是你的时间特征张量形状完全不符合模型要求。
2. 数据集类核心修正
你的TimeSeriesDataset对future_values、past_time_features的处理完全错误,修改后的类如下:
class TimeSeriesDataset(Dataset): def __init__(self, X, y, observed_mask): self.X = X self.y = y self.observed_mask = observed_mask self.num_time_features = 1 # 用序列位置作为时间特征,无真实时间特征时设为1 def __len__(self): return len(self.X) def __getitem__(self, idx): # past_values形状: [context_length, input_size] = [10,70] past_values = self.X[idx] context_length = past_values.shape[0] # future_values形状: [prediction_length, output_size] = [1,2] future_values = self.y[idx].unsqueeze(0) # 生成时间特征:用序列位置编码作为简单时间特征 past_time_features = torch.arange(context_length).unsqueeze(1).float() # [10,1] future_time_features = torch.arange(context_length, context_length + 1).unsqueeze(1).float() # [1,1] # 观察掩码形状与对应值保持一致 past_observed_mask = self.observed_mask[idx] future_observed_mask = torch.ones_like(future_values) # 训练时标签已知,设为1 sample = { 'past_values': past_values, 'past_time_features': past_time_features, 'past_observed_mask': past_observed_mask, 'future_values': future_values, 'future_time_features': future_time_features, 'future_observed_mask': future_observed_mask } return sample
3. 模型配置修正
num_time_features需与数据集里的时间特征数一致,不能等于输入特征数:
config = TimeSeriesTransformerConfig( prediction_length=1, context_length=look_back, lags_seq=[1, 2, 3], input_size=data_transformed.shape[1], # 70,正确 output_size=2, # 正确,预测2个特征 num_time_features=1, # 修正为时间特征数 num_static_categorical_features=0, num_static_real_features=0, cardinality=[], embedding_dimension=[] )
4. 预测后处理修正
模型输出形状为[batch_size, prediction_length, output_size],需压缩维度后再逆变换:
# Evaluation predictions, labels, _ = trainer.predict(test_dataset) predictions = predictions.squeeze(1) # 从[batch_size,1,2]转为[batch_size,2] # 构造完整特征矩阵进行逆变换(scaler基于全特征训练) def inverse_transform_predictions(preds, scaler, total_features): dummy = np.zeros((preds.shape[0], total_features)) dummy[:, -2:] = preds return scaler.inverse_transform(dummy)[:, -2:] predictions = inverse_transform_predictions(predictions, scaler, data_transformed.shape[1]) labels = inverse_transform_predictions(y_test.numpy(), scaler, data_transformed.shape[1])
5. 其他细节检查
- 确认
create_dataset中目标列data[i + look_back, -2:]确实是你要预测的特征 - 观察掩码
past_observed_mask无缺失值时用全1是正确的
内容的提问来源于stack exchange,提问作者bolt investor

