项目背景
当前,常见的大气污染预测模型大多是基于物理机理构建的,比如空气质量预测模型 Calpuff、AERMOD、CMAQ 等。然而,这些模型运算较为复杂,对于输入数据的要求非常高,运算耗时也比较长,适合用于常规固定区域的预报。当遇到突发污染事件时,就无法有效发挥作用。
针对以上问题,本项目以某城区 3km*3km 范围的固定模拟区域,根据污染物扩散模型,快速计算任意释放点源和任意风向的污染物扩散动图,并进行精度评估。仅利用城市局部污染物扩散云图作为输入,使用深度学习模型提取图像中污染物扩散的特征,纯数据驱动,无需建立物理模型,预测耗时短,适合作为突发污染扩散事件时的应急处置决策辅助。
项目需求
课题名称
课题需求
项目地址
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
from paddle.nn.utils import weight_norm
# 创建基础卷积层
def create_layer(in_channels, out_channels, kernel_size, wn=True, bn=True,
activation=nn.ReLU, convolution=nn.Conv2D):
assert kernel_size % 2 == 1
layer = [ ]
conv = convolution(in_channels, out_channels, kernel_size, padding=kernel_size // 2)
if wn:
conv = weight_norm(conv)
layer.append(conv)
if activation is not None:
layer.append(activation())
if bn:
layer.append(nn.BatchNorm2D(out_channels))
return nn.Sequential(*layer)
# 创建Encoder中的单个块
def create_encoder_block(in_channels, out_channels, kernel_size, wn=True, bn=True,
activation=nn.ReLU, layers=2):
encoder = [ ]
for i in range(layers):
_in = out_channels
_out = out_channels
if i == 0:
_in = in_channels
encoder.append(create_layer(_in, _out, kernel_size, wn, bn, activation, nn.Conv2D))
return nn.Sequential(*encoder)
# 创建Decoder中的单个块
def create_decoder_block(in_channels, out_channels, kernel_size, wn=True, bn=True,
activation=nn.ReLU, layers=2, final_layer=False):
decoder = [ ]
for i in range(layers):
_in = in_channels
_out = in_channels
_bn = bn
_activation = activation
if i == 0:
_in = in_channels * 2
if i == layers - 1:
_out = out_channels
if final_layer:
_bn = False
_activation = None
decoder.append(create_layer(_in, _out, kernel_size, wn, _bn, _activation, nn.Conv2DTranspose))
return nn.Sequential(*decoder)
# 创建Encoder
def create_encoder(in_channels, filters, kernel_size, wn=True, bn=True, activation=nn.ReLU, layers=2):
encoder = [ ]
for i in range(len(filters)):
if i == 0:
encoder_layer = create_encoder_block(in_channels, filters[i], kernel_size, wn, bn, activation, layers)
else:
encoder_layer = create_encoder_block(filters[i - 1], filters[i], kernel_size, wn, bn, activation, layers)
encoder = encoder + [encoder_layer]
return nn.Sequential(*encoder)
# 创建Decoder
def create_decoder(out_channels, filters, kernel_size, wn=True, bn=True, activation=nn.ReLU, layers=2):
decoder = []
for i in range(len(filters)):
if i == 0:
decoder_layer = create_decoder_block(filters[i], out_channels, kernel_size, wn, bn, activation, layers,
final_layer= True)
else:
decoder_layer = create_decoder_block(filters[i], filters[i - 1], kernel_size, wn, bn, activation, layers,
final_layer= False)
decoder = [decoder_layer] + decoder
return nn.Sequential(*decoder)
# 创建网络
class UNetEx(nn.Layer):
def __init__(self, in_channels, out_channels, kernel_size=3, filters=[16, 32, 64], layers=3,
weight_norm=True, batch_norm=True, activation=nn.ReLU, final_activation=None):
super().__init__()
assert len(filters) > 0
self.final_activation = final_activation
self.encoder = create_encoder(in_channels, filters, kernel_size, weight_norm, batch_norm, activation, layers)
decoders = [ ]
# for i in range(out_channels):
decoders.append(create_decoder(out_channels, filters, kernel_size, weight_norm, batch_norm, activation, layers))
self.decoders = nn.Sequential(*decoders)
def encode(self, x):
tensors = [ ]
indices = [ ]
sizes = [ ]
for encoder in self.encoder:
x = encoder(x)
sizes.append(x.shape)
tensors.append(x)
x, ind = F.max_pool2d(x, 2, 2, return_mask= True)
indices.append(ind)
return x, tensors, indices, sizes
def decode(self, _x, _tensors, _indices, _sizes):
y = [ ]
for _decoder in self.decoders:
x = _x
tensors = _tensors[:]
indices = _indices[:]
sizes = _sizes[:]
for decoder in _decoder:
tensor = tensors.pop()
size = sizes.pop()
ind = indices.pop()
# 反池化操作,为上采样
x = F.max_unpool2d(x, ind, 2, 2, output_size=size)
x = paddle.concat([tensor, x], axis= 1)
x = decoder(x)
y.append(x)
return paddle.concat(y, axis= 1)
def forward(self, x):
x, tensors, indices, sizes = self.encode(x)
x = self.decode(x, tensors, indices, sizes)
if self.final_activation is not None:
x = self.final_activation(x)
return x
# 训练方法
def train(model, train_dataset, criterion, optimizer, device, num_epochs):
loss_history = [ ]
epoch_loss = 0
# 遍历批次
for epoch in range(num_epochs):
optimizer.clear_grad()
for batch_id in range(len(train_dataset) -1):
inputs = train_dataset[batch_id]
outputs_true = train_dataset[batch_id+ 1]
inputs = T.ToTensor()(inputs)
inputs = paddle.unsqueeze(inputs, 0)
outputs_true = T.ToTensor()(outputs_true)
outputs_true = paddle.unsqueeze(outputs_true, 0)
# 训练
outputs = model(inputs)
# 计算损失值
loss = criterion(outputs, outputs_true)
if batch_id % 10 == 0:
print( 'epoch:',epoch, 'batch_id:',batch_id, 'loss:',loss.numpy())
loss.backward()
epoch_loss += loss.item()
optimizer.step()
epoch_loss /= len(train_dataset)
loss_history.append(epoch_loss)
print( "Epoch [{}/{}], Loss: {:.8f}".format(epoch + 1, num_epochs, loss.numpy()[ 0]))
# 保存模型
if epoch % 10 == 0:
save_model(model, '/home/aistudio/pollution_model.pdparams')
print( "Training complete.")
return loss_history
def test():
# 初始化结果列表
results = [ ]
# 测试集合起始点
inputs = test_dataset[ 0]
inputs = T.ToTensor()(inputs)
inputs = paddle.unsqueeze(inputs, 0)
# 是否supervise
flag_supervise = True
device = paddle.set_device( 'gpu' if paddle.is_compiled_with_cuda() else 'cpu')
# 加载模型
model = UNetEx( 3, 3, 3)
load_model(model, '/home/aistudio/pollution_model.pdparams',device)
for num in range( 1, 10):
# 进行预测
outputs = model(inputs)
outputs_np = outputs.numpy()
outputs_np = np.squeeze(outputs_np, axis= 0) # 去除第一个维度(batch_size)
outputs_np = np.transpose(outputs_np, ( 1, 2, 0)) # 将通道维度调整为最后一个维度
outputs_np = ( 255 * np.clip(outputs_np, 0, 1)).astype( 'uint8')
#outputs_np = outputs_np.transpose([1, 2, 0])
#outputs_np_uint8 = (outputs_np * 255).astype(np.uint8)
# 将预测结果添加到结果列表中
results.append(outputs_np)
if flag_supervise == False:
# 将预测结果作为下一帧的输入
inputs = outputs
else:
# 使用真实数据预测
inputs = test_dataset[num+ 1]
inputs = T.ToTensor()(inputs)
inputs = paddle.unsqueeze(inputs, 0)
return results
results = test()
如图 5 所示,浓度误差主要集中在污染源附近(如图红色框),主要数值分布在-0.02~0.02 之间。不同颜色分别代表不同浓度区间误差,蓝色表示的低浓度相对误差较小,绿色红色表示的中高浓度误差平均误差较高,绿色区域表征的中等浓度区域,偏大的误差影响的面积较大。
基于前一时刻的污染物浓度云图,预测后十个时刻、二十个时刻,四十个时刻的污染物浓度云图;
尝试用多时刻预测多时刻。
相关地址