GFPGAN
GFPGAN copied to clipboard
I want export "GFPGANCleanv1-NoCE-C2.pth" to ONNX
I want to use dynamic weights in F.conv2d in pytorch.
def forward(self, x, style):
b, c, h, w = x.shape # c = c_in
style = self.modulation(style).view(b, 1, c, 1, 1)
weight = self.weight * style # (b, c_out, c_in, k, k)
weight = weight.view(b * self.out_channels, c, self.kernel_size, self.kernel_size)
b, c, h, w = x.shape
x = x.view(1, b * c, h, w)
out = F.conv2d(x, weight, padding=self.padding, groups=b)
out = out.view(b, self.out_channels, *out.shape[2:4])
return out
However, only constant weights are supported in Barracuda.
How can I change F.conv2d?
+1
+1
+1
same question
+1
+1
Hi @jjeamin , here some research to convert GFPGANv1.3-to-ncnn
This should be work, I covert operations on weight to input(x), then GFPGAN model will be constant. 这种方法实测有效,我将对weight的操作等效修改为对x的操作,这样卷积的权重就是固定参数,模型就会从动态转换为静态。
class ModulatedConv2d(nn.Module):
def __init__(self,
in_channels,
out_channels,
kernel_size,
num_style_feat,
demodulate=True,
sample_mode=None,
eps=1e-8):
super(ModulatedConv2d, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.kernel_size = kernel_size`
self.demodula`te = demodulate
self.sample_mode = sample_mode
self.eps = eps
# modulation inside each modulated conv
self.modulation = nn.Linear(num_style_feat, in_channels, bias=True)
# initialization
default_init_weights(self.modulation, scale=1, bias_fill=1, a=0, mode='fan_in', nonlinearity='linear')
self.weight = nn.Parameter(
torch.randn(1, out_channels, in_channels, kernel_size, kernel_size) /
math.sqrt(in_channels * kernel_size**2))
self.padding = kernel_size // 2
self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=self.padding, bias=False)
self.conv2d.weight.data = self.weight.view(1 * self.out_channels, self.in_channels,self.kernel_size,self.kernel_size)
def forward(self, x, style):
b, c, h, w = x.shape # c = c_in
# weight modulation
style = self.modulation(style).view(b, c, 1, 1)
x = x * style
if self.demodulate:
if self.sample_mode == 'upsample':
x = F.interpolate(x, scale_factor=2, mode='bilinear', align_corners=False)
elif self.sample_mode == 'downsample':
x = F.interpolate(x, scale_factor=0.5, mode='bilinear', align_corners=False)
x = self.conv2d(x)
weight = self.weight * style
demod = torch.rsqrt(weight.pow(2).sum([2, 3, 4]) + self.eps)
out = x * demod.view(b, self.out_channels, 1, 1)
else:
if self.sample_mode == 'upsample':
x = F.interpolate(x, scale_factor=2, mode='bilinear', align_corners=False)
elif self.sample_mode == 'downsample':
x = F.interpolate(x, scale_factor=0.5, mode='bilinear', align_corners=False)
x = x.view(1, b * c, h, w)
out = self.conv2d(x)
out = out.view(b, self.out_channels, *out.shape[2:4])
return out
some test converting for stylegan2
Here project with GFPGANv1.3.onnx gfpgan Link to model GFPGANv1.3.onnx.prototxt
WOW Many Thanks!!!!
This should be work, I covert operations on weight to input(x), then GFPGAN model will be constant. 这种方法实测有效,我将对weight的操作等效修改为对x的操作,这样卷积的权重就是固定参数,模型就会从动态转换为静态。
class ModulatedConv2d(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, num_style_feat, demodulate=True, sample_mode=None, eps=1e-8): super(ModulatedConv2d, self).__init__() self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = kernel_size` self.demodula`te = demodulate self.sample_mode = sample_mode self.eps = eps # modulation inside each modulated conv self.modulation = nn.Linear(num_style_feat, in_channels, bias=True) # initialization default_init_weights(self.modulation, scale=1, bias_fill=1, a=0, mode='fan_in', nonlinearity='linear') self.weight = nn.Parameter( torch.randn(1, out_channels, in_channels, kernel_size, kernel_size) / math.sqrt(in_channels * kernel_size**2)) self.padding = kernel_size // 2 self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=self.padding, bias=False) self.conv2d.weight.data = self.weight.view(1 * self.out_channels, self.in_channels,self.kernel_size,self.kernel_size) def forward(self, x, style): b, c, h, w = x.shape # c = c_in # weight modulation style = self.modulation(style).view(b, c, 1, 1) x = x * style if self.demodulate: if self.sample_mode == 'upsample': x = F.interpolate(x, scale_factor=2, mode='bilinear', align_corners=False) elif self.sample_mode == 'downsample': x = F.interpolate(x, scale_factor=0.5, mode='bilinear', align_corners=False) x = self.conv2d(x) weight = self.weight * style demod = torch.rsqrt(weight.pow(2).sum([2, 3, 4]) + self.eps) out = x * demod.view(b, self.out_channels, 1, 1) else: if self.sample_mode == 'upsample': x = F.interpolate(x, scale_factor=2, mode='bilinear', align_corners=False) elif self.sample_mode == 'downsample': x = F.interpolate(x, scale_factor=0.5, mode='bilinear', align_corners=False) x = x.view(1, b * c, h, w) out = self.conv2d(x) out = out.view(b, self.out_channels, *out.shape[2:4]) return out
did it work for converting to onnx and the sesults are equal to the torcch model?