Proyecto PytorchCNN build 8 - retropropagación

Retropropagación

La retropropagación se puede dividir en función de activación, capa completamente conectada, capa convolucional, capa de agrupación, etc.

1. Función de activación

  • Función de activación sigmoidea, las otras funciones de activación son las mismas.
    Derivada de la función de activación sigmoidea
class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self,x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        return out

    def backward(self,dout):
        dx = dout * (1.0 - self.out) * self.out
        return dx

2. Capa completamente conectada

class Affine:
    def __init__(self,W,b):
        self.W = W
        self.b = b
        self.x = None
        self.original_x_shape = None

        self.dW = None
        self.db = None

    def forward(self,x):
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0],-1)
        self.x = x

        out = np.dot(self.x, self.W) + self.b
        return out

    def backward(self,dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        dx = dx.reshape(*self.original_x_shape)
        return dx

3. Capa convolucional

class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        #
        self.x = None
        self.col = None
        self.col_W = None

        #dw & db
        self.dW = None
        self.db = None

    def forward(self, x):
        FN, C, FH, FW = self.W.shape #W[FN,C,FH,FW]  FN:滤波器个数 C:通道数 FH:滤波器高 FW:滤波器的宽
        N, C, H, W = x.shape #x[N,C,H,W] N:输入图像的个数 C:通道数 H:图像的高 W:图像的宽

        out_h = 1 + int((H + 2*self.pad - FH) / self.stride) #输出h
        out_w = 1 + int((W + 2*self.pad - FW) / self.stride) #输出w

        col = im2col(x, FH, FW, self.stride, self.pad) #x-->col col:[N*out_h*out_w, C*FH*FW]
        col_W = self.W.reshape(FN,-1).T #W-->col_W col_W:[FN, C*FH*FW] --> col_W:[C*FH*FW, FN]

        out = np.dot(col,col_W) #out=col*col_W  out:[N*out_h*out_w, FN]
        out = out.reshape(N, out_h, out_w, -1).transpose(0,3,1,2) #out:[N,out_h,out_w, FN] --> [N, FN, out_h, out_w]

        self.x = x #x:[N,C,H,W]
        self.col = col #col:[N*out_h*out_w, C*FH*FW]
        self.col_W = col_W #col_W:[C*FH*FW, FN]

        return out

    def backward(self, dout):
        FN,C,FH,FW = self.W.shape
        dout =  dout.transpose(0,2,3,1).reshape(-1,FN) #dout:[N,FN,out_h,out_w] --> [N,out_h,out_w,FN] --> [N*out_h*out_w,FN]

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout) #col:[N*out_h*out_w, C*FH*FW] --> [C*FH*FW,N*out_h*out_w]  dW:[N*out_h*out_w,FN]
        self.dW = self.dW.transpose(1,0).reshape(FN,C,FH,FW) #dW:[FN,C,FH,FW]

        dcol = np.dot(dout, self.col_W.T) #dcol:[N*out_h*out_w,C*FH*FW]
        dx = col2im(dcol,self.x.shape, FH,FW,self.stride,self.pad) #dx:[N,C,out_h,out_w]

        return dx

Nota: Se utilizan dos funciones muy importantes en la capa convolucional:

  1. En la propagación hacia adelante, es: im2col, es decir, la imagen se convierte en un valor para realizar la operación de convolución.
  2. En retropropagación, es: col2im, lo que significa que el valor se convierte en una imagen para realizar la operación de deconvolución.
  • Ponemos estas dos funciones en utils y las llamamos en esta función.
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    """

    Args:
        input_data: 由(N, C, H, W)__(数据量,通道数,图片高,图片宽)的4维数组构成的输入数据
        filter_h: 滤波器的高
        filter_w: 滤波器的宽
        stride: 步长,默认为1
        pad: 填充,默认为0

    Returns: col 2维数组

    """
    N, C, H, W = input_data.shape #100,1,28,28
    out_h = (H + 2*pad - filter_h)//stride + 1 #28
    out_w = (W + 2*pad - filter_w)//stride + 1 #28
    # pdb.set_trace()

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')#img.shape = 100,1,30,30
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w)) # N, C, filter_h, filter_w, out_h, out_w

    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    # pdb.set_trace()
    # col.shape: 100,1,3,3,28,28 N, C, filter_h, filter_w, out_h, out_w --> N, out_h, out_w, C, filter_h, filter_w,
    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1) #N, out_h, out_w, C, filter_h, filter_w --> N*out_h*out_w, C*filter_h*filter_w
    return col # N*out_h*out_w, C*filter_h*filter_w


def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    """

    Args:
        col: 2维数组 N*out_h*out_w, C*filter_h*filter_w
        input_shape: 由(N, C, H, W)__(数据量,通道数,图片高,图片宽)的4维数组构成的输入数据
        filter_h: 滤波器的高
        filter_w: 滤波器的宽
        stride: 步长,默认为1
        pad: 填充,默认为0

    Returns:img

    """
    N, C, H, W = input_shape
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)

    img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]


4. Agrupación

class Pooling:
    def __init__(self,pool_h,pool_w, stride=1,pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    def forward(self, x):
        N,C,H,W = x.shape

        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = im2col(x,self.pool_h,self.pool_w,self.stride,self.pad)
        col = col.reshape(-1, self.pool_h*self.pool_w)

        arg_max = np.argmax(col, axis=1) #获取每一行最大值的索引值
        out = np.max(col,axis=1) #获取每一行最大值
        out = out.reshape(N,out_h,out_w,C).transpose(0,3,1,2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self,dout):
        dout = dout.transpose(0,2,3,1)

        pool_size = self.pool_h * self.pool_w
        dmax =np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()

        # pdb.set_trace()

        dmax = dmax.reshape(dout.shape + (pool_size,)) #dout.shape[2,5,5,16] pool_size=4
        # dmax = dmax.reshape((dout.size,pool_size))

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] *dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h,self.pool_w,self.stride,self.pad)

        return dx

referencias

  1. Retropropagación de la red neuronal BP
  2. Red neuronal convolucional CNN backpropagation
  3. "Introducción al aprendizaje profundo: teoría e implementación basadas en Python"

Supongo que te gusta

Origin blog.csdn.net/qq_44783177/article/details/113952105
Recomendado
Clasificación