transformer·注意力

 
在seq2seq2中使用RNN提取序列特征
- RNN是有前后依赖关系的,因此不能并行运算
- 注意力替换RNN后可以并行运算了,但不再有前后依赖关系了 

 
解决办法是 通过位置编码 加入 位置信息 
- 因为有了位置信息,相同的单词处于不同的位置上时,信息是不一样的 
- 位置编码数的数值不能太大,毕竟向量的值的范围为[-1,1]

    

 

    

 
在resnet中, 做了一个k=1的卷积才做为短接的主线 

在transformer中,直接对原始数据做深度clone,做为短接的主线
- 这意味着,梯度几乎没有消失
- 意味着,神经网络可以有很多层 

辅助线有两个
- 一个是注意力计算,直接将注意力计算的结果+主线
- 一个是全连接,直接将全连接的计算结果+主线 

    

 

    

 

    

 
层归一化放在了主线的clone之后
- 随着层数增加,主线的变化只有一个
- 就是将辅线的计算结果加上主线上
- 这可能导致主线变化较大,所以辅线计算时,先做一个层归一化
- 如此,辅线的起点近似相同,处理后的结果也都在0附近,[-1,1]

 
-------------------------------------------------------------------------------

 


 

  

 


时序数据

 
 
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.preprocessing import MinMaxScaler

# 生成模拟时序数据
def generate_time_series(start_date, num_steps, freq='D'):
        timestamp = pd.date_range(start=start_date, periods=num_steps, freq=freq)
        trend = np.linspace(0, 5, num_steps)
        seasonality = 10 * np.sin(np.linspace(0, 10*np.pi, num_steps))
        noise = np.random.normal(0, 1, num_steps)
        value = trend + seasonality + noise
        
        return pd.DataFrame({
            'timestamp': timestamp,
            'value': value.astype(np.float32),
            'feature1': np.random.uniform(0, 10, num_steps),  # 附加随机特征
            'feature2': np.random.randint(0, 5, num_steps)
        }).set_index('timestamp')

# 参数配置
SEQ_LENGTH = 100  # 滑动窗口长度
BATCH_SIZE = 64
HIDDEN_SIZE = 32
EPOCHS = 50

# 生成数据
df = generate_time_series('2025-01-01', 100)
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df.values)

分钟

 
import pandas as pd

# 示例1:指定起止时间生成分钟序列
time_index_1 = pd.date_range(
    start='2023-01-01 08:00', 
    end='2024-01-01 08:05', 
    freq='T'
)

# 示例2:通过起始时间+数量生成序列
time_index_2 = pd.date_range(
    start='2023-01-01 09:00', 
    periods=120,  # 生成120个时间点(120分钟)
    freq='T'
)

# 示例3:生成15分钟间隔序列
time_index_3 = pd.date_range(
    start='2023-01-01 10:00', 
    periods=4, 
    freq='15T'  # 输出为10:00, 10:15, 10:30, 10:45
)

# 转换为DataFrame
df = pd.DataFrame(index=time_index_1)
df.shape  #(525606, 0)

 
df[:7]

2023-01-01 08:00:00
2023-01-01 08:01:00
2023-01-01 08:02:00
2023-01-01 08:03:00
2023-01-01 08:04:00
2023-01-01 08:05:00
2023-01-01 08:06:00

秒级

 
import pandas as pd

timestamps = pd.date_range(
    start='2024-04-12 13:57:32',
    end='2025-04-12 13:57:42',
    freq='S'
)
timestamps.shape  #(31536011,)
    

 
import pandas as pd

timestamps = pd.date_range(
    start='2024-04-12 13:57:32',
    end='2025-04-12 13:57:42',
    freq='10S'
)
timestamps.shape  #(3153602,)
    

 

    

 
import pandas as pd
import numpy as np 


# 生成模拟时序数据
def generate_time_series():
    
    time_index_1 = pd.date_range(
        start='2023-01-01 08:00', 
        end='2024-01-01 08:05', 
        freq='min'
    )
    num_steps = time_index_1.shape[0]
    trend = np.linspace(0, 5, num_steps)
    seasonality = 10 * np.sin(np.linspace(0, 10*np.pi, num_steps))
    noise = np.random.normal(0, 1, num_steps)

    #将多个特征合并为一个时序
    value = trend + seasonality + noise
    return pd.DataFrame({
            'timestamp': time_index_1,
            'value': value.astype(np.float32),
            'feature1': np.random.uniform(0, 10, num_steps),  # 附加随机特征
            'feature2': np.random.randint(0, 5, num_steps)
        }).set_index('timestamp')
    
        
# 生成数据
df = generate_time_series()
df

    

 
多个维度的数据相加合并为一列,再融入一些噪声
    

 

    

 

    

 

    

 
多个维度的数据合并,同时融合了target,
目标是将target中分离出来

从序列中分离出另外一个序列 

 
import torch
import pandas as pd
import numpy as np 

# 生成模拟时序数据
def generate_time_series11():
    
    time_index_1 = pd.date_range(
        start='2023-01-01 08:00', 
        end='2024-01-01 08:05', 
        freq='min'
    )
    num_steps = time_index_1.shape[0]
    trend = np.linspace(0, 5, num_steps)
    seasonality = 10 * np.sin(np.linspace(0, 10*np.pi, num_steps))
    noise = np.random.normal(0, 1, num_steps)

    #[low,high)
    #这里一个交易对应一个类别,一个序列中有seq_len条交易,这个类别也形成一个序列
    target = 3 * np.sin(np.linspace(0, 7*np.pi, num_steps))
    

    #将多个特征合并为一个时序
    value = trend + seasonality + noise + target
    return pd.DataFrame({
            'timestamp': time_index_1,
            'value': value.astype(np.float32),
            'feature1': np.random.uniform(0, 10, num_steps),  # 附加随机特征
            'feature2': np.random.randint(0, 5, num_steps)
        }).set_index('timestamp'),pd.DataFrame(target,columns=['target'])
    
        
# 生成数据
df_X,df_y = generate_time_series11()
df = df_X


 
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df.values)

# 参数配置
SEQ_LENGTH = 100  # 滑动窗口长度
BATCH_SIZE = 64
HIDDEN_SIZE = 32
EPOCHS = 50


 
import torch

# 构建训练集
def create_sequences11(data,label_seq, seq_length):
    
        sequences = []
        targets = []
        iv = seq_length//10
        for i in range(0,len(data)-seq_length,iv):
            sequences.append(data[i:i+seq_length])
            targets.append(label_seq[i:i+seq_length])  # 预测主特征
        return torch.FloatTensor(sequences), torch.FloatTensor(targets)

label_seq = np.array(df_y)
X, y = create_sequences11(scaled_data, label_seq, SEQ_LENGTH)

X.shape,y.shape
(torch.Size([52551, 100, 3]), torch.Size([52551, 100, 1]))
  

 

  

 

  

序列的长度

 
LSTM 通常处理20个长度的序列

Transformer没有此限制,但通常不超过50,
实际上是大模型无限制,但小模型序列长了准备率仍不高
  

编码字典

 
原项目 X一套字典,y一套字典,
即编码序列一套字典,解码序列一套字典

原项目是一对一转换之后又做了逆序处理,
个人没有做逆序处理,在一一对应的情况下分别编码,导致a与A的编码一致
- 这使得转化后的数据序列字母本就一致,进而让模型参数还没有学习损失函数的值就非常低
- 损失函数低完全是编码巧合导致的,并不是模型具备了很好的预测能力 

  

补码

 
n = random.randint(pc.min_len_seq, pc.max_len_seq-2)  #减的2是给起始标记的

补码尽量不使用,也就是说序列长度基本接近于 序列的最大长度
- 如果补码过多,就会出现一个个补码相连的情况,这会让模型认为补码后就是补码,因为模型算的是概率

  

 


 


 


时序·Xy31

 
from tpf.datasets import SeqXy31
from torch.utils.data import DataLoader

dataset = SeqXy31(seq_len=100)
trainDataLoader = DataLoader(dataset=dataset,batch_size=64,shuffle=True,drop_last=True)

 
for X,y in trainDataLoader:
  print(X.shape,y.shape)       #torch.Size([64, 100, 3]) torch.Size([64, 100, 1])
  break 

 

    

X,y皆序列:从多组序列中分解出一个序列

 
import torch
import pandas as pd
import numpy as np 
from tpf.datasets import sd

X,y = sd.getXy31(seq_len=100)
X.shape,y.shape   #(torch.Size([52551, 100, 3]), torch.Size([52551, 100, 1])) 


sd:seq data,序列数据

 
正弦时序数据
- X:主特征value:三个特征+一个随机噪声;两个随机特征,feature1,feature2;做了归一化处理
- y:主特征value中的一个正弦时序

 

    

 

    

 

    

 
一个seq元素[seq_len, col_nums],即一个X[i] 由一个3维的向量表示,即用3个元素表示一条数据

[batch_size, seq_len, col_nums] --  [64, 100, 1]
- 将X[i] 变换到 1维度的数值上,也就是回归问题 

 
如果是交易,那么就是seq len个交易组合成一个序列,将这个序列看作一个元素  
- Xy31的y也是一个序列,其元素与X中的元素按索引一一对应 
- 本质上还是X[i] -- y[i]的映射 
- [batch_size,xi] -- [batch_size, yi]的映射 xi与xj之间是相互独立的 

[batch_size, seq_len, col_nums] --  [64, seq_len, 1]
- 将[seq_len, col_nums]看作一个元素时,每个X[i] -- y[i]的映射是结合了整个seq的信息的 
- 是X[i]结合了X[0]...X[i-1],X[i+1]...X[n-1]的信息映射到y[i]的 

 


 
Xy31实际上是由正弦函数+线性函数生成的数据 
每个X[i] 都可以对应一个时间函数t(i) 

这实际上是一个时序数据,有多个维度,每个维度有自己的频率 

 
从频率的角度看交易 
- 在时间函数t(i)上产生一条交易数据 
- 每个交易有多个频率的信号组合而成  
- 即将频率与列对应起来,一个列可看作是相同频率的函数在时间上的展开  

 


 
Xy31一个元素X[i]有3个特征组成,这与图像的像素相似 
- 图像中一个像素就是3个元素,然后多个像素组成一张图片,就是一个元素  
- 这里一条交易就是一个元素,多个元素组成一个序列 
- 不同的是图像是2维的,序列是1维的 ,稍微有些不同 
  

 
但仔细想想,序列中真实表达的意思,对世界的描述,是多维的,复杂结构的 
  

 

  

 


时序·模型

 
import pandas as pd
import numpy as np 
import torch
from torch import nn

from tpf.datasets import SeqXy31
from torch.utils.data import DataLoader

class pc:
    batch_size = 64
    seq_len=100  
    col_nums = 3
    embedding_dim=128
    class_nums = 1
    
dataset = SeqXy31(seq_len=100)
trainDataLoader = DataLoader(dataset=dataset,batch_size=pc.batch_size,shuffle=True,drop_last=True)
dataset[0][0][0],dataset[1][0][0]

 
 (tensor([0.3810, 0.4219, 1.0000]), tensor([0.4002, 0.0629, 1.0000]))
    

 
for X,y in trainDataLoader:
  print(X.shape,y.shape)       #torch.Size([64, 100, 3]) torch.Size([64, 100, 1])
  break 
    

 
这是个回归问题,每个数据元素映射到一个数值 
    

 

    

 
from tpf.nlp.ts13 import SeqTrans

#每个元素由seq_len个特征组成,每个特征的维度输入时为in_feature_dim,输出为out_feature_dim,在embedding_dim上变换
model = SeqTrans(seq_len=pc.seq_len, in_feature_dim=pc.col_nums, out_feature_dim=pc.class_nums, embedding_dim=pc.embedding_dim)
loss_fn = nn.MSELoss()
optim = torch.optim.Adam(params= model.parameters(),lr=1e-4) 
    

 

for X,y in trainDataLoader:
  y_out  = model(X)
  print(y_out.shape,y.shape)  # torch.Size([64, 100, 1]) torch.Size([64, 100, 1]) 
  
  loss = loss_fn(y_out,y)
  loss.backward()   #求参数导
  
  optim.step()      #参数调整
  optim.zero_grad() #清空参数梯度
  
  print(round(loss.item(),5))  #5.5041
  break 
    

 
一个seq元素,即一个X[i] 由一个3维的向量表示,即用3个元素表示一条数据

[batch_size, seq_len, col_nums] --  [64, 100, 1]
- 将X[i] 变换到 1维度的数值上,也就是回归问题 

 

    

 

    

 


 

  

 


参考