(1)这并非是官方代码,只是本人一时兴趣,对其网络模型进行复现,不喜勿喷
(2)pytorch 版本
import torch
import torch.nn as nn
import torch.nn.functional as F
class GraphNodes(nn.Module):
def __init__(self,fin,fout):
super(GraphNodes,self).__init__()
self.fin = fin # 2
self.fout = fout # 16
self.thresold = 1e-12 # eps
self.fc = nn.Linear(self.fin,self.fout,bias=False)
self.leakyrelu = nn.LeakyReLU(0.2)
self.bias = nn.Parameter(torch.Tensor(self.fout)) # 自定义参数 偏置
def euclidean_dist(self,x): # 求解欧式距离
bs = x.shape[0] # bs
x_m = x.unsqueeze(1).repeat(1,bs,1) # [bs,bs,2] 减数
x_l = x.unsqueeze(0).repeat(bs,1,1) # [bs,bs,2] 被减数
dist = torch.pow(x_l-x_m,2).sum(2).clamp(min=self.thresold).sqrt() # 欧式距离
dist_ = torch.exp(-1*dist) # 距离的负数为幂,e为底
return dist_
def get_adj(self,dist): # 求解领接矩阵
bs = dist.shape[0] # bs
A = F.softmax(dist,1) # 列归一化
Alsum = A.sum(0) # 行求和
Afinall = torch.zeros(bs,bs,dtype=torch.float32) # 预先初始化最后的领接矩阵
for i in range(bs):
for j in range(bs):
for k in range(bs):
Afinall[i][j] = Afinall[i][j] + A[i][k]*A[j][k]/Alsum[k]
return Afinall # 返回第一个领接矩阵
def forward(self,x): # x:[bs,2] 表示bs个行人,2表示(x,y) (相对位置)
dist = self.euclidean_dist(x) # 欧式距离矩阵
A = self.get_adj(dist) # 归一化的领接矩阵
s = self.fc(x) # [bs,fout]
h = self.leakyrelu(torch.matmul(A,s)+self.bias) # [bs,fout]
return h
class MultiHeadGraphAttention(nn.Module): # 多头图注意力模型
def __init__(self, n_head, f_in, f_out, attn_dropout, bias=True):
super(MultiHeadGraphAttention, self).__init__()
self.n_head = n_head # 头大小
self.f_in = f_in # 输入大小
self.f_out = f_out # 输出大小
self.isbias = bias # 偏置
self.w = nn.Parameter(torch.Tensor(n_head, f_in, f_out)) # 自定义参数 权重
self.a_src = nn.Parameter(torch.Tensor(n_head, f_out, 1)) # 自定义参数
self.a_dst = nn.Parameter(torch.Tensor(n_head, f_out, 1)) # 自定义参数
self.leaky_relu = nn.LeakyReLU(negative_slope=0.2) # 激活函数
self.softmax = nn.Softmax(dim=-1) # 归一层
self.dropout = nn.Dropout(attn_dropout) # Dropout 层
self.bias = nn.Parameter(torch.Tensor(f_out)) # 自定义参数 偏置
nn.init.constant_(self.bias, 0) # 初始化参数
# 初始化自定义参数
nn.init.xavier_uniform_(self.w, gain=1.414)
nn.init.xavier_uniform_(self.a_src, gain=1.414)
nn.init.xavier_uniform_(self.a_dst, gain=1.414)
def forward(self, h):
bs = h.shape[0] # [bs]
h_prime = torch.matmul(h, self.w) # [head,bs,f_out]
attn_src = torch.matmul(h_prime, self.a_src) # [head,bs,1]
attn_dst = torch.matmul(h_prime, self.a_dst) # [head,bs,1]
attn = attn_src.expand(-1, -1, bs) + attn_dst.expand(-1, -1, bs).permute( # 每个行人对当前行人的影响
0, 2, 1
) # [head,bs,bs]
attn = self.leaky_relu(attn)
attn = self.softmax(attn)
attn = self.dropout(attn)
output = torch.matmul(attn, h_prime) # [head,bs,f_out]
if self.isbias:
output = output + self.bias
output = output.permute(1,0,2) # [bs,head,f_out]
output = torch.reshape(output,(bs,-1)) # [bs,head*f_out]
return output
class ATTENTIONBLOCK(nn.Module):
def __init__(self, n_units, n_heads, dropout=0.2):
super(ATTENTIONBLOCK, self).__init__()
self.n_layer = len(n_units) - 1 # 中间层大小
self.dropout = dropout
self.layer_stack = nn.ModuleList() # 模型列表
for i in range(self.n_layer):
f_in = n_units[i] * n_heads[i - 1] if i else n_units[i] # 多头输入
self.layer_stack.append( # 添加图注意力层
MultiHeadGraphAttention(
n_heads[i], f_in=f_in, f_out=n_units[i + 1], attn_dropout=dropout
)
)
def forward(self, x):
for _, att_layer in enumerate(self.layer_stack):
x = att_layer(x)
return x
class EGAT(nn.Module):
def __init__(self,fin,n_units,n_heads):
super(EGAT,self).__init__()
self.fin = fin # [2]
self.n_units = n_units # [16,16,16]
self.n_heads = n_heads # [4,1]
self.att_block = ATTENTIONBLOCK(self.n_units,self.n_heads) # 注意力模型
self.nodes = GraphNodes(self.fin,self.n_units[0]) # 获得结点特征
def forward(self,x):
x = self.nodes(x)
x = self.att_block(x)
return x
class TCN(nn.Module):
def __init__(self,fin,fout,layers=3,ksize=3):
super(TCN,self).__init__()
self.fin = fin # [48]
self.fout = fout # [48]
self.layers = layers # [3]
self.ksize = ksize # [3]
self.convs = nn.ModuleList() # 空洞卷积
for i in range(self.layers):
self.convs.append(nn.Conv1d(self.fin,self.fout,kernel_size=self.ksize))
def forward(self,x):
for _, conv_layer in enumerate(self.convs):
# left padding
x = nn.functional.pad(x,(self.ksize-1,0))
x = conv_layer(x)
return x
class Encoder(nn.Module): # 这里使用残差连接
def __init__(self,fin,fout,layers=3,ksize=3):
super(Encoder,self).__init__()
self.fin = fin # [48]
self.fout = fout # [48]
self.layers = layers # [3]
self.ksize = ksize # [3]
self.tcnf = TCN(self.fin,self.fout,self.layers,self.ksize)
self.tcng = TCN(self.fin,self.fout,self.layers,self.ksize)
def forward(self,x):
residual = x
f = torch.sigmoid(self.tcnf(x))
g = torch.tanh(self.tcng(x))
return residual+f*g # residual connection
class EncoderBlock(nn.Module): # 这里使用跳连接
def __init__(self,fin,fout,blocks=3,layers=3,ksize=3):
super(EncoderBlock,self).__init__()
self.fin = fin # [48]
self.fout = fout # [48]
self.blocks = blocks # [3]
self.layers = layers # [3]
self.ksize = ksize # [3]
self.encoders = nn.ModuleList() # 空洞卷积
for i in range(self.blocks):
self.encoders.append(Encoder(self.fin,self.fout,self.layers,self.ksize)) # 添加多个Encoder模块
def forward(self,x):
s = 0
for _, en_layer in enumerate(self.encoders):
x = en_layer(x)
s = s + x # skip connection
return s
class Embedding(nn.Module): # 这里使用绝对位置,对位置进行编码
def __init__(self,fin,fout):
super(Embedding,self).__init__()
self.fin = fin # [2]
self.fout = fout # [32]
self.fc = nn.Linear(self.fin,self.fout,bias=False)
def forward(self,x):
return self.fc(x)
class Decoder(nn.Module): # 生成相对位置
def __init__(self,obs_len,pre_len,fin,fout,noise_dim,M=20):
super(Decoder,self).__init__()
self.obs_len = obs_len # 12
self.pre_len = pre_len # 8
self.fin = fin # 48
self.fout = fout # 2
self.noise_dim = noise_dim # 16
self.M = M # 20
self.mlp = nn.Sequential( # 多层感知机
nn.Linear(self.fin*self.obs_len+self.noise_dim,390),
nn.LeakyReLU(0.2),
nn.Linear(390,204),
nn.LeakyReLU(0.2),
nn.Linear(204,self.pre_len*self.fout),
nn.LeakyReLU(0.2)
)
def forward(self,x):
Y = []
for i in range(self.M): # 预测多条合理的轨迹
noise = torch.randn(x.shape[0],self.noise_dim) # 随机噪声
h = torch.cat((x,noise),1) # 添加噪声
h = torch.reshape(self.mlp(h),(-1,self.pre_len,self.fout)) # [bs,pre,2]
Y.append(h)
return torch.stack(Y,0) # [M,bs,pre,2]
class GraphTCN(nn.Module): # 这里使用跳连接
def __init__(self,fin,fout,n_units,n_heads,obs_len,pre_len,noise_dim,M,blocks,n_layers,ksize):
super(GraphTCN,self).__init__()
self.fin = fin # 2 x,y
self.fout = fout # [32] 绝对位置嵌入维度
self.n_units = n_units # [16,16,16]
self.n_heads = n_heads # [4,1]
self.obs_len = obs_len # 12
self.pre_len = pre_len # 8
self.noise_dim = noise_dim #16
self.M = M # 20
self.blocks = blocks # [3]
self.n_layers = n_layers # [3]
self.ksize = ksize # [3]
self.egat = EGAT(self.fin,self.n_units,self.n_heads) # 相对位置嵌入
self.embedding = Embedding(self.fin,self.fout) # 绝对位置嵌入
self.encoder = EncoderBlock(self.fout+self.n_units[-1],self.fout+self.n_units[-1],self.blocks,self.n_layers,self.ksize) # 编码器
self.decoder = Decoder(self.obs_len,self.pre_len,self.fout+self.n_units[-1],self.fin,self.noise_dim,self.M) # 解码器
def forward(self,xre,xab): # xre,xab [len,bs,2]
bs = xre.shape[1]
egats, embeddings = [],[]
for i in range(xre.shape[0]):
egats.append(self.egat(xre[i]))
embeddings.append(self.embedding(xab[i]))
egats = torch.stack(egats,-1) # [bs,16,len]
embeddings = torch.stack(embeddings,-1) # [bs,32,len]
x = torch.cat((egats,embeddings),1) # [bs,48,len]
x = self.encoder(x) # [bs,hs,len]
x = self.decoder(torch.reshape(x,(bs,-1))) # [M,bs,pre,2]
return x
g = GraphTCN(fin=2,fout=32,n_units=[16,16,16], n_heads=[4,1],obs_len=12,pre_len=8,
noise_dim=16,M=20,blocks=3,n_layers=3,ksize=3)
xre = torch.randn(12,5,2)
xab = torch.randn(12,5,2)
print(g(xre,xab).shape)
(3)tensorflow2 版本
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers,initializers
import numpy as np
class GraphNodes(keras.Model):
def __init__(self,fout):
super(GraphNodes,self).__init__()
self.fout = fout # 16
self.thresold = 1e-12 # eps
self.fc = layers.Dense(self.fout,use_bias=False)
self.leakyrelu = layers.LeakyReLU(alpha=0.2)
self.initializer = initializers.GlorotUniform()
self.bias = tf.Variable(self.initializer(shape=[self.fout], dtype=tf.float32))
def euclidean_dist(self,x): # 求解欧式距离
bs = x.shape[0] # bs
x_m = tf.tile(tf.expand_dims(x,1),[1,bs,1]) # [bs,bs,2] 减数
x_l = tf.tile(tf.expand_dims(x,0),[bs,1,1]) # [bs,bs,2] 被减数
dist = tf.reduce_sum(tf.pow(x_l-x_m,2),2)
dist = tf.sqrt(tf.clip_by_value(dist,self.thresold,tf.float32.max)) # 欧式距离
dist_ = tf.exp(-1*dist) # 距离的负数为幂,e为底
return dist_
def get_adj(self,dist): # 求解领接矩阵,注意tensorflow中的tensor不能直接赋值
bs = dist.shape[0] # bs
A = tf.nn.softmax(dist,1).numpy() # 列归一化
Alsum = A.sum(0) # 行求和
Afinall = np.zeros((bs,bs),dtype=float) # 预先初始化最后的领接矩阵
for i in range(bs):
for j in range(bs):
for k in range(bs):
Afinall[i][j] = Afinall[i][j] + A[i][k]*A[j][k]/Alsum[k]
return tf.convert_to_tensor(Afinall,dtype=tf.float32) # 返回第一个领接矩阵
def call(self,x): # x:[bs,2] 表示bs个行人,2表示(x,y) (相对位置)
dist = self.euclidean_dist(x) # 欧式距离矩阵
A = self.get_adj(dist) # 归一化的领接矩阵
s = self.fc(x) # [bs,fout]
h = self.leakyrelu(tf.matmul(A,s)+self.bias) # [bs,fout]
return h
class MultiHeadGraphAttention(keras.Model): # 多头图注意力模型
def __init__(self, n_head, f_in, f_out, attn_dropout, bias=True):
super(MultiHeadGraphAttention, self).__init__()
self.n_head = n_head # 头大小
self.f_in = f_in # 输入大小
self.f_out = f_out # 输出大小
self.isbias = bias # 偏置
self.initializer = initializers.GlorotUniform() # 初始化分布
self.w = tf.Variable(self.initializer(shape=[self.n_head, self.f_in, self.f_out], dtype=tf.float32)) # 自定义参数 权重
self.a_src = tf.Variable(self.initializer(shape=[self.n_head, self.f_out, 1], dtype=tf.float32)) # 自定义参数
self.a_dst = tf.Variable(self.initializer(shape=[self.n_head, self.f_out, 1], dtype=tf.float32)) # 自定义参数
self.leaky_relu = layers.LeakyReLU(alpha=0.2) # 激活函数
self.softmax = layers.Softmax(axis=-1) # 归一层
self.dropout = layers.Dropout(rate=attn_dropout) # Dropout 层
if self.isbias:
self.bias = tf.Variable(tf.zeros(self.f_out)) # 自定义参数 偏置
def call(self, h):
bs = h.shape[0] # [bs]
h_prime = tf.matmul(h, self.w) # [head,bs,f_out]
attn_src = tf.matmul(h_prime, self.a_src) # [head,bs,1]
attn_dst = tf.matmul(h_prime, self.a_dst) # [head,bs,1]
attn = tf.tile(attn_src,[1,1,bs]) + tf.transpose(tf.tile(attn_dst,[1,1,bs]),[0, 2, 1]) # 每个行人对当前行人的影响
# [head,bs,bs]
attn = self.leaky_relu(attn)
attn = self.softmax(attn)
attn = self.dropout(attn)
output = tf.matmul(attn, h_prime) # [head,bs,f_out]
if self.isbias:
output = output + self.bias
output = tf.transpose(output,[1,0,2]) # [bs,head,f_out]
output = tf.reshape(output,[bs,-1]) # [bs,head*f_out]
return output
class ATTENTIONBLOCK(keras.Model):
def __init__(self, n_units, n_heads, dropout=0.2):
super(ATTENTIONBLOCK, self).__init__()
self.n_layer = len(n_units) - 1 # 中间层大小
self.dropout = dropout
self.layer_stack = [] # 模型列表
for i in range(self.n_layer):
f_in = n_units[i] * n_heads[i - 1] if i else n_units[i] # 多头输入
self.layer_stack.append( # 添加图注意力层
MultiHeadGraphAttention(
n_heads[i], f_in=f_in, f_out=n_units[i + 1], attn_dropout=self.dropout
)
)
def call(self, x):
for _, att_layer in enumerate(self.layer_stack):
x = att_layer(x)
return x
class EGAT(keras.Model):
def __init__(self,n_units,n_heads):
super(EGAT,self).__init__()
self.n_units = n_units # [16,16,16]
self.n_heads = n_heads # [4,1]
self.att_block = ATTENTIONBLOCK(self.n_units,self.n_heads) # 注意力模型
self.nodes = GraphNodes(self.n_units[0]) # 获得结点特征
def call(self,x):
x = self.nodes(x)
x = self.att_block(x)
return x
class TCN(keras.Model):
def __init__(self,fout,n_layers=3,ksize=3):
super(TCN,self).__init__()
self.fout = fout # [48]
self.n_layers = n_layers # [3]
self.ksize = ksize # [3]
self.paddings = tf.constant([[0,0],[self.ksize-1,0], [0, 0]])
self.convs = [] # 空洞卷积
for i in range(self.n_layers):
self.convs.append(layers.Conv1D(self.fout,kernel_size=self.ksize))
def call(self,x):
for _, conv_layer in enumerate(self.convs):
# left padding
x = tf.pad(x, self.paddings, "CONSTANT")
x = conv_layer(x)
return x
class Encoder(keras.Model): # 这里使用残差连接
def __init__(self,fout,n_layers=3,ksize=3):
super(Encoder,self).__init__()
self.fout = fout # [48]
self.n_layers = n_layers # [3]
self.ksize = ksize # [3]
self.tcnf = TCN(self.fout,self.n_layers,self.ksize)
self.tcng = TCN(self.fout,self.n_layers,self.ksize)
def call(self,x):
residual = x
f = tf.sigmoid(self.tcnf(x))
g = tf.tanh(self.tcng(x))
return residual+f*g # residual connection
class EncoderBlock(keras.Model): # 这里使用跳连接
def __init__(self,fout,blocks=3,n_layers=3,ksize=3):
super(EncoderBlock,self).__init__()
self.fout = fout # [48]
self.blocks = blocks # [3]
self.n_layers = n_layers # [3]
self.ksize = ksize # [3]
self.encoders = [] # 空洞卷积
for i in range(self.blocks):
self.encoders.append(Encoder(self.fout,self.n_layers,self.ksize)) # 添加多个Encoder模块
def call(self,x):
s = 0
for _, en_layer in enumerate(self.encoders):
x = en_layer(x)
s = s + x # skip connection
return s
class Embedding(keras.Model): # 这里使用绝对位置,对位置进行编码
def __init__(self,fout):
super(Embedding,self).__init__()
self.fout = fout # [32]
self.fc = layers.Dense(self.fout,use_bias=False)
def call(self,x):
return self.fc(x)
class Decoder(keras.Model): # 生成相对位置
def __init__(self,pre_len,fout,noise_dim,M=20):
super(Decoder,self).__init__()
self.pre_len = pre_len # 8
self.fout = fout # 2
self.noise_dim = noise_dim # 16
self.M = M # 20
self.mlp = keras.Sequential() # 多层感知机
self.mlp.add(layers.Dense(390))
self.mlp.add(layers.LeakyReLU(alpha=0.2))
self.mlp.add(layers.Dense(204))
self.mlp.add(layers.LeakyReLU(alpha=0.2))
self.mlp.add(layers.Dense(self.pre_len*self.fout))
self.mlp.add(layers.LeakyReLU(alpha=0.2))
def call(self,x):
Y = []
for i in range(self.M): # 预测多条合理的轨迹
noise = tf.random.normal([x.shape[0],self.noise_dim]) # 随机噪声
h = tf.concat([x,noise],1) # 添加噪声
h = tf.reshape(self.mlp(h),[-1,self.pre_len,self.fout]) # [bs,pre,2]
Y.append(h)
return tf.stack(Y,0) # [M,bs,pre,2]
class GraphTCN(keras.Model): # 这里使用跳连接
def __init__(self,fin,fout,n_units,n_heads,pre_len,noise_dim,M,blocks,n_layers,ksize):
super(GraphTCN,self).__init__()
self.fin = fin # 2 x,y
self.fout = fout # [32] 绝对位置嵌入维度
self.n_units = n_units # [16,16,16]
self.n_heads = n_heads # [4,1]
self.pre_len = pre_len # 8
self.noise_dim = noise_dim #16
self.M = M # 20
self.blocks = blocks # [3]
self.n_layers = n_layers # [3]
self.ksize = ksize # [3]
self.egat = EGAT(self.n_units,self.n_heads) # 相对位置嵌入
self.embedding = Embedding(self.fout) # 绝对位置嵌入
self.encoder = EncoderBlock(self.fout+self.n_units[-1],self.blocks,self.n_layers,self.ksize) # 编码器
self.decoder = Decoder(self.pre_len,self.fin,self.noise_dim,self.M) # 解码器
def call(self,xre,xab): # xre,xab [len,bs,2]
bs = xre.shape[1]
egats, embeddings = [],[]
for i in range(xre.shape[0]):
egats.append(self.egat(xre[i]))
embeddings.append(self.embedding(xab[i]))
egats = tf.stack(egats,-1) # [bs,16,len]
embeddings = tf.stack(embeddings,-1) # [bs,32,len]
x = tf.concat([egats,embeddings],1) # [bs,48,len]
x = self.encoder(tf.transpose(x,[0,2,1])) # [bs,hs,len]
x = self.decoder(tf.reshape(x,[bs,-1])) # [M,bs,pre,2]
return x
g = GraphTCN(fin=2,fout=32,n_units=[16,16,16], n_heads=[4,1],pre_len=8,
noise_dim=16,M=20,blocks=3,n_layers=3,ksize=3)
xre = tf.random.normal([12,5,2])
xab = tf.random.normal([12,5,2])
print(g(xre,xab).shape)