forked from ermongroup/CSDI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiff_models.py
More file actions
194 lines (161 loc) · 8.57 KB
/
Copy pathdiff_models.py
File metadata and controls
194 lines (161 loc) · 8.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from linear_attention_transformer import LinearAttentionTransformer
def get_torch_trans(heads=8, layers=1, channels=64):#创建Transformer编码器层的,使用标准的PyTorch Transformer编码器
encoder_layer = nn.TransformerEncoderLayer(
d_model=channels, nhead=heads, dim_feedforward=64, activation="gelu"
)
return nn.TransformerEncoder(encoder_layer, num_layers=layers)
def get_linear_trans(heads=8,layers=1,channels=64,localheads=0,localwindow=0):
#去掉了softmax部分,近似矩阵计算k*Q,提高计算效率
return LinearAttentionTransformer(
dim = channels,
depth = layers,
heads = heads,
max_seq_len = 256,
n_local_attn_heads = 0,
local_attn_window_size = 0,
)
def Conv1d_with_init(in_channels, out_channels, kernel_size):
layer = nn.Conv1d(in_channels, out_channels, kernel_size)
nn.init.kaiming_normal_(layer.weight)#初始化layer层的权重
return layer
class DiffusionEmbedding(nn.Module):
#定义了一个用于生成扩散步骤的嵌入向量的模块。它利用正弦和余弦函数生成周期性的位置编码,并通过两个全连接层进行投影
def __init__(self, num_steps, embedding_dim=128, projection_dim=None):
#参数num_steps用来表示扩散步骤的数量
super().__init__()
if projection_dim is None:
projection_dim = embedding_dim
#注册了一个名为 "embedding" 的缓冲区,用于存储预计算的嵌入向量。
#persistent=False 表示这个缓冲区不会被保存到模型的状态字典中(即不会被保存到磁盘)
self.register_buffer(
"embedding",#名字就叫embedding,使用的时候直接调用embedding
self._build_embedding(num_steps, embedding_dim / 2),
persistent=False,
)
self.projection1 = nn.Linear(embedding_dim, projection_dim)
self.projection2 = nn.Linear(projection_dim, projection_dim)
def forward(self, diffusion_step):
#这里的diffusion_step才是随机生成的时间t
x = self.embedding[diffusion_step]
x = self.projection1(x)
x = F.silu(x)
x = self.projection2(x)
x = F.silu(x)
return x
def _build_embedding(self, num_steps, dim=64):
steps = torch.arange(num_steps).unsqueeze(1) # (T,1)
frequencies = 10.0 ** (torch.arange(dim) / (dim - 1) * 4.0).unsqueeze(0) # (1,dim)
table = steps * frequencies # (T,dim),这里*就表示矩阵乘法
table = torch.cat([torch.sin(table), torch.cos(table)], dim=1) # (T,dim*2)
return table
class diff_CSDI(nn.Module):
def __init__(self, config, inputdim=2):
super().__init__()
self.channels = config["channels"]
self.diffusion_embedding = DiffusionEmbedding(
num_steps=config["num_steps"],
embedding_dim=config["diffusion_embedding_dim"],
)
self.input_projection = Conv1d_with_init(inputdim, self.channels, 1)#kernel_size=1
self.output_projection1 = Conv1d_with_init(self.channels, self.channels, 1)
self.output_projection2 = Conv1d_with_init(self.channels, 1, 1)
nn.init.zeros_(self.output_projection2.weight)
self.residual_layers = nn.ModuleList(
[
ResidualBlock(
side_dim=config["side_dim"],
channels=self.channels,
diffusion_embedding_dim=config["diffusion_embedding_dim"],
nheads=config["nheads"],
is_linear=config["is_linear"],
)
for _ in range(config["layers"])
] #参数layers=4,即一共有4个ResidualBlock
)
def forward(self, x, cond_info, diffusion_step):
B, inputdim, K, L = x.shape
x = x.reshape(B, inputdim, K * L)
x = self.input_projection(x)#一维卷积改变通道数至channel
x = F.relu(x)#激活函数
x = x.reshape(B, self.channels, K, L)
diffusion_emb = self.diffusion_embedding(diffusion_step)
#forward中的函数diffusion_step给该对象的forward函数传参数
skip = []
for layer in self.residual_layers:
x, skip_connection = layer(x, cond_info, diffusion_emb)
skip.append(skip_connection) #用之前创建的skip存储skip_connection块数据
x = torch.sum(torch.stack(skip), dim=0) / math.sqrt(len(self.residual_layers))
#将skip连成一个块后,对第一个维度求和,即(4,B,channel,K*L)->(B,channel,K*L),然后整体除以math.sqrt(len(self.residual_layers))
x = x.reshape(B, self.channels, K * L)
x = self.output_projection1(x) # (B,channel,K*L),不改变通道数的一维卷积
x = F.relu(x)
x = self.output_projection2(x) # (B,1,K*L),将通道数降为1的一维卷积
x = x.reshape(B, K, L)#将数据还原为(B, K, L)
return x
class ResidualBlock(nn.Module):
def __init__(self, side_dim, channels, diffusion_embedding_dim, nheads, is_linear=False):
super().__init__()
self.diffusion_projection = nn.Linear(diffusion_embedding_dim, channels)
self.cond_projection = Conv1d_with_init(side_dim, 2 * channels, 1)
self.mid_projection = Conv1d_with_init(channels, 2 * channels, 1)
self.output_projection = Conv1d_with_init(channels, 2 * channels, 1)
self.is_linear = is_linear
if is_linear:
self.time_layer = get_linear_trans(heads=nheads,layers=1,channels=channels)
self.feature_layer = get_linear_trans(heads=nheads,layers=1,channels=channels)
else:
self.time_layer = get_torch_trans(heads=nheads, layers=1, channels=channels)
self.feature_layer = get_torch_trans(heads=nheads, layers=1, channels=channels)
def forward_time(self, y, base_shape):#时间注意力提取层
B, channel, K, L = base_shape
if L == 1:
return y
y = y.reshape(B, channel, K, L).permute(0, 2, 1, 3).reshape(B * K, channel, L)
if self.is_linear:
y = self.time_layer(y.permute(0, 2, 1)).permute(0, 2, 1)
else:
y = self.time_layer(y.permute(2, 0, 1)).permute(1, 2, 0)
#y.permute(2, 0, 1)得到(L,B*K,channel),然后再得到(B*K,channel,L)
y = y.reshape(B, K, channel, L).permute(0, 2, 1, 3).reshape(B, channel, K * L)
#展开回(B, K, channel, L),然后变成(B,channel,K,L),最后变成(B, channel, K * L)
return y
def forward_feature(self, y, base_shape):#特征注意力提取层
B, channel, K, L = base_shape
if K == 1:
return y
y = y.reshape(B, channel, K, L).permute(0, 3, 1, 2).reshape(B * L, channel, K)
#(B, channel, K, L)->(B, L, channel, K)->(B * L, channel, K)
if self.is_linear:
y = self.feature_layer(y.permute(0, 2, 1)).permute(0, 2, 1)
else:
y = self.feature_layer(y.permute(2, 0, 1)).permute(1, 2, 0)
#(K, B * L, channel)->(B*L, channel, K)
y = y.reshape(B, L, channel, K).permute(0, 2, 3, 1).reshape(B, channel, K * L)
#(B, L, channel, K)->(B, channel, K, L)->(B, channel, K * L)
return y
def forward(self, x, cond_info, diffusion_emb):
B, channel, K, L = x.shape
base_shape = x.shape
x = x.reshape(B, channel, K * L)
diffusion_emb = self.diffusion_projection(diffusion_emb).unsqueeze(-1) # (B,channel,1)
#这里直接通过线性层改变通道后,利用广播机制与输入数据x直接相加
y = x + diffusion_emb
y = self.forward_time(y, base_shape) # (B,channel,K*L)
y = self.forward_feature(y, base_shape) # (B,channel,K*L)
y = self.mid_projection(y) # (B,2*channel,K*L)
_, cond_dim, _, _ = cond_info.shape
cond_info = cond_info.reshape(B, cond_dim, K * L)
cond_info = self.cond_projection(cond_info) # (B,2*channel,K*L)
y = y + cond_info
gate, filter = torch.chunk(y, 2, dim=1)#分割数据y为两部分,且dim=1为从channel维度处划分
y = torch.sigmoid(gate) * torch.tanh(filter) # (B,channel,K*L)
y = self.output_projection(y) # (B,2*channel,K*L)
residual, skip = torch.chunk(y, 2, dim=1)#用同样的方法再次划分数据
x = x.reshape(base_shape)
residual = residual.reshape(base_shape)
skip = skip.reshape(base_shape)
return (x + residual) / math.sqrt(2.0), skip#返回两个参数,一个为前一个块同输入x的和,另一个为后一个块