Convolution kernel weight of self defined neural network in Python

Keywords: Python network

1. Convolution kernel weight of self defined neural network

Neural network is deeply loved by deep learners. One of the reasons is the convenience of neural network. Users only need to build neural network framework like building blocks according to their own needs. In the process of building, we only need to consider the size of convolution core, the number of input and output channels, convolution mode and so on.

When we are used to using our own parameters, when we want to customize the convolution kernel parameters, suddenly there is a feeling of no way to start, ha ha ha ha ha ha ha ha ~ ~, please allow me to be happy, ha ha ha! Because I met the same problem when I first entered the neural network, I stepped on too many pits at that time, and my baby wanted to cry! What makes me sad is that I searched all the resource areas and didn't find any sharing. Therefore, I want to write out my method, hoping to help you baby, happy (* ^ ▽ ^ *).

Don't say much, the text begins

2. Define convolution kernel weight

Here is the self-defined convolution kernel weight of dtt coefficient, directly up the weight code:

2.1 dtt coefficient weight Code

Def DTT matrix (n): this function is the DTT coefficient matrix of n*n, and the author's is the coefficient matrix of 8 * 8.

Def DTT kernel (out channels, in channels, kernel size): this method is to set the weight. The weight needs to include four parameters (output channel number, input channel number, convolution kernel height, convolution kernel width). There are many details to be noted here. The babies have to lie down in the pit to have a deep image. I won't go into it.

import numpy as np
import torch
import torch.nn as nn


# ================================
# DTT coefficient matrix of n * n
# ================================
def dtt_matrix(n):
    dtt_coe = np.zeros([n, n], dtype='float32')
    for i in range(0, n):
        dtt_coe[0, i] = 1/np.sqrt(n)
        dtt_coe[1, i] = (2*i + 1 - n)*np.sqrt(3/(n*(np.power(n, 2) - 1)))
    for i in range(1, n-1):
        dtt_coe[i+1, 0] = -np.sqrt((n-i-1)/(n+i+1)) * np.sqrt((2*(i+1)+1)/(2*(i+1)-1)) * dtt_coe[i, 0]
        dtt_coe[i+1, 1] = (1 + (i+1)*(i+2)/(1-n)) * dtt_coe[i+1, 0]
        dtt_coe[i+1, n-1] = np.power(-1, i+1) * dtt_coe[i+1, 0]
        dtt_coe[i+1, n-2] = np.power(-1, i+1) * dtt_coe[i+1, 1]
        for j in range(2, int(n/2)):
            t1 = (-(i+1) * (i+2) - (2*j-1) * (j-n-1) - j)/(j*(n-j))
            t2 = ((j-1) * (j-n-1))/(j * (n-j))
            dtt_coe[i+1, j] = t1 * dtt_coe[i+1, j-1] + t2 * dtt_coe[i+1, j-2]
            dtt_coe[i+1, n-j-1] = np.power(-1, i-1) * dtt_coe[i+1, j]
    return dtt_coe


# ===============================================================
# DTT coefficient matrix of (out_channels * in_channels * n * n)
# ===============================================================
def dtt_kernel(out_channels, in_channels, kernel_size):
    dtt_coe = dtt_matrix(kernel_size)
    dtt_coe = np.array(dtt_coe)

    dtt_weight = np.zeros([out_channels, in_channels, kernel_size, kernel_size], dtype='float32')
    temp = np.zeros([out_channels, in_channels, kernel_size, kernel_size], dtype='float32')

    order = 0
    for i in range(0, kernel_size):
        for j in range(0, kernel_size):
            dtt_row = dtt_coe[i, :]
            dtt_col = dtt_coe[:, j]
            dtt_row = dtt_row.reshape(len(dtt_row), 1)
            dtt_col = dtt_col.reshape(1, len(dtt_col))
            # print("dtt_row: ", dtt_row)
            # print("dtt_col: ", dtt_col)
            # print("i:", i, "j: ", j)
            temp[order, 0, :, :] = np.dot(dtt_row, dtt_col)
            order = order + 1
    for i in range(0, in_channels):
        for j in range(0, out_channels):
            # dtt_weight[j, i, :, :] = flip_180(temp[j, 0, :, :])
            dtt_weight[j, i, :, :] = temp[j, 0, :, :]
    return torch.tensor(dtt_weight)

 

2.2 'same' convolution

If the baby needs to keep the data size before and after convolution unchanged, that is, convolution in'same'mode, then you can use my convolution kernel directly.

import torch.utils.data
from torch.nn import functional as F
import math
import torch
from torch.nn.parameter import Parameter
from torch.nn.functional import pad
from torch.nn.modules import Module
from torch.nn.modules.utils import _single, _pair, _triple

class _ConvNd(Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride,
                 padding, dilation, transposed, output_padding, groups, bias):
        super(_ConvNd, self).__init__()
        if in_channels % groups != 0:
            raise ValueError('in_channels must be divisible by groups')
        if out_channels % groups != 0:
            raise ValueError('out_channels must be divisible by groups')
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.transposed = transposed
        self.output_padding = output_padding
        self.groups = groups
        if transposed:
            self.weight = Parameter(torch.Tensor(
                in_channels, out_channels // groups, *kernel_size))
        else:
            self.weight = Parameter(torch.Tensor(
                out_channels, in_channels // groups, *kernel_size))
        if bias:
            self.bias = Parameter(torch.Tensor(out_channels))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        n = self.in_channels
        for k in self.kernel_size:
            n *= k
        stdv = 1. / math.sqrt(n)
        self.weight.data.uniform_(-stdv, stdv)
        if self.bias is not None:
            self.bias.data.uniform_(-stdv, stdv)

    def __repr__(self):
        s = ('{name}({in_channels}, {out_channels}, kernel_size={kernel_size}'
             ', stride={stride}')
        if self.padding != (0,) * len(self.padding):
            s += ', padding={padding}'
        if self.dilation != (1,) * len(self.dilation):
            s += ', dilation={dilation}'
        if self.output_padding != (0,) * len(self.output_padding):
            s += ', output_padding={output_padding}'
        if self.groups != 1:
            s += ', groups={groups}'
        if self.bias is None:
            s += ', bias=False'
        s += ')'
        return s.format(name=self.__class__.__name__, **self.__dict__)

class Conv2d(_ConvNd):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1,
                 padding=0, dilation=1, groups=1, bias=True):
        kernel_size = _pair(kernel_size)
        stride = _pair(stride)
        padding = _pair(padding)
        dilation = _pair(dilation)
        super(Conv2d, self).__init__(
            in_channels, out_channels, kernel_size, stride, padding, dilation,
            False, _pair(0), groups, bias)
    def forward(self, input):
        return conv2d_same_padding(input, self.weight, self.bias, self.stride,
                        self.padding, self.dilation, self.groups)

# custom con2d, because pytorch don't have "padding='same'" option.

def conv2d_same_padding(input, weight, bias=None, stride=1, padding=1, dilation=1, groups=1):
    input_rows = input.size(2)
    filter_rows = weight.size(2)
    effective_filter_size_rows = (filter_rows - 1) * dilation[0] + 1
    out_rows = (input_rows + stride[0] - 1) // stride[0]

    input_cols = input.size(3)
    filter_cols = weight.size(3)
    effective_filter_size_cols = (filter_cols - 1) * dilation[1] + 1
    out_cols = (input_cols + stride[1] - 1) // stride[1]

    padding_needed = max(0, (out_rows - 1) * stride[0] + effective_filter_size_rows -input_rows)
    padding_rows = max(0, (out_rows - 1) * stride[0] +
                        (filter_rows - 1) * dilation[0] + 1 - input_rows)
    rows_odd = (padding_rows % 2 != 0)
    padding_cols = max(0, (out_cols - 1) * stride[1] +
                       (filter_cols - 1) * dilation[1] + 1 - input_cols)
    cols_odd = (padding_cols % 2 != 0)
    if rows_odd or cols_odd:
        input = pad(input, [0, int(cols_odd), 0, int(rows_odd)])
    return F.conv2d(input, weight, bias, stride,
                  padding=(padding_rows // 2, padding_cols // 2),
                  dilation=dilation, groups=groups)

 

2.3 assign weight to convolution kernel

This is what the babies care about most. Don't panic. Here comes ha, happy (* ^ ^ *), and into the main body.

Here is a simple network model (a fixed convolution + 3 full connections, the full connection is 1 * 1 Conv2d). I give a comment in the code, the babies should be able to understand every second, (*)!

import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import dtt_kernel
import util
import paddingSame

# Define weights
dtt_weight1 = dtt_kernel.dtt_kernel(64, 2, 8)


class DttNet(nn.Module):
    def __init__(self):
        super(DttNet, self).__init__()
self.conv1
= paddingSame.Conv2d(2, 64, 8)
     # Assign weight to convolution kernel self.conv1.weight
= nn.Parameter(dtt_weight1, requires_grad=False) self.fc1 = util.fc(64, 512, 1) self.fc2 = util.fc(512, 128, 1) self.fc3 = util.fc(128, 2, 1, last=True) def forward(self, x): x = self.conv1(x) x = self.fc1(x) x = self.fc2(x) x = self.fc3(x) return x

 

2.4 supplement my util class

import torch.nn as nn


def conv(in_channels, out_channels, kernel_size, stride=1, dilation=1, batch_norm=True):
    if batch_norm:
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=(kernel_size // 2)),
            nn.BatchNorm2d(out_channels),
            nn.ReLU()
        )
    else:
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=(kernel_size // 2)),
            nn.ReLU()
        )


def fc(in_channels, out_channels, kernel_size, stride=1, bias=True, last=False):
    if last:
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=(kernel_size // 2)),
        )
    else:
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=(kernel_size // 2)),
            nn.BatchNorm2d(out_channels),
            nn.ReLU()
        )

 

 

3. Summary

Wow, I'm finished. I don't know if the babies have any harvest. o((⊙)) o, o (⊙)) o. You can leave a message if you don't understand. I will often pay attention to my garden. If there's something wrong with it, the babies are also squeaking at me in the message area. We'll see you next time.

Posted by geoffism on Mon, 04 May 2020 22:12:44 -0700