6.3.3 互相关运算¶

In [4]:
# codes06001_cross_correlation
import paddle

def cross_correlation(X, W):
    h, w = W.shape
    Y = paddle.zeros((X.shape[0]-h+1, X.shape[1]-w+1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i:i+h, j:j+w]*W).sum()
    return Y

X = paddle.to_tensor([[1.0,2.0,3.0,4.0], [5.0,6.0,7.0,8.0], [9.0,10.0,11.0,12.0]])
W = paddle.to_tensor([[1.0,2.0], [3.0,4.0]])
cross_correlation(X, W)
Out[4]:
Tensor(shape=[2, 3], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[44. , 54. , 64. ],
        [84. , 94. , 104.]])

6.3.4 图像的卷积层¶

In [5]:
import sys
sys.path.append(r'D:\WorkSpace\DeepLearning\WebsiteV2')   # 定义自定义模块保存位置
from codes.paddle import common
import paddle
In [6]:
# codes06002_Conv2D
class Conv2D(paddle.nn.Layer):
    def __init__(self, kernel_size):
        super().__init__()
        self.weight = paddle.ParamAttr(paddle.rand(kernel_size))
        self.bias = paddle.ParamAttr(paddle.zeros(1))

    def forward(self, x):
        Y = common.cross_correlation(x, self.weight) + self.bias
        return Y        

6.3.5 基于卷积的边缘检测¶

  • 定义输入样本
In [7]:
import paddle

# 生成9×9的垂直条纹图
X = paddle.ones((9, 9))
X[:, 3:6] = 0
print(X) # 输出图像矩阵
Tensor(shape=[9, 9], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.],
        [1., 1., 1., 0., 0., 0., 1., 1., 1.]])
  • 可视化输入样本:条纹图(9×9)
In [8]:
import matplotlib.pylab as plt
plt.figure(figsize=(3,3))
plt.imshow(X, cmap=plt.cm.gray)
Out[8]:
<matplotlib.image.AxesImage at 0x25824331fd0>
  • 定义三种边缘检测器(卷积核)
In [9]:
# 3×3的垂直边缘检测器
W1 = paddle.to_tensor([[1,0,-1], [1,0,-1], [1,0,-1]])
# 2×2的垂直边缘检测器
W2 = paddle.to_tensor([[1,-1], [1,-1]])
# 2×2的水平边缘检测器
W3 = paddle.to_tensor([[1,1], [-1,-1]])
  • 输出三种垂直边缘检测器获得的互相关运算结果
In [10]:
# 使用3×3垂直边缘检测器获得的边缘
Y1 = common.cross_correlation(X, W1)
# 使用2×2垂直边缘检测器获得的边缘
Y2 = common.cross_correlation(X, W2)
# 使用2×2水平边缘检测器获得垂直条纹图
Y3 = common.cross_correlation(X, W3)
print('{} \n {} \n {}'.format(Y1, Y2, Y3))
Tensor(shape=[7, 7], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[ 0.,  3.,  3.,  0., -3., -3.,  0.],
        [ 0.,  3.,  3.,  0., -3., -3.,  0.],
        [ 0.,  3.,  3.,  0., -3., -3.,  0.],
        [ 0.,  3.,  3.,  0., -3., -3.,  0.],
        [ 0.,  3.,  3.,  0., -3., -3.,  0.],
        [ 0.,  3.,  3.,  0., -3., -3.,  0.],
        [ 0.,  3.,  3.,  0., -3., -3.,  0.]]) 
 Tensor(shape=[8, 8], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.],
        [ 0.,  0.,  2.,  0.,  0., -2.,  0.,  0.]]) 
 Tensor(shape=[8, 8], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.]])
  • 可视化输出矩阵
In [11]:
plt.figure(figsize=(24,6))
ax = plt.subplot(1,4,1)
plt.imshow(X, cmap=plt.cm.gray)
ax.set_title('Original Image', fontsize=20)
ax = plt.subplot(1,4,2)
plt.imshow(Y1)
ax.set_title('Y1: 3×3 Vertical Edge Detector', fontsize=20)
ax = plt.subplot(1,4,3)
plt.imshow(Y2)
ax.set_title('Y2: 2×2 Vertical Edge Detector', fontsize=20)
ax = plt.subplot(1,4,4)
plt.imshow(Y3)
ax.set_title('Y3: 2×2 Horizontal Edge Detector', fontsize=20)
Out[11]:
Text(0.5, 1.0, 'Y3: 2×2 Horizontal Edge Detector')
  • 旋转输入,并输出水平检测器的结果
In [12]:
# 使用2×2垂直边缘检测器检测垂直条纹图
Y3 = common.cross_correlation(X.T, W2)
# 使用2×2水平边缘检测器获得垂直条纹图
Y4 = common.cross_correlation(X.T, W3)
print('{} \n {}'.format(Y3, Y4))
Tensor(shape=[8, 8], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.]]) 
 Tensor(shape=[8, 8], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
        [ 2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.],
        [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
        [-2., -2., -2., -2., -2., -2., -2., -2.],
        [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

6.3.6. 学习卷积核¶

In [14]:
# codes06007_EdgeDetection_LearnKernel
# 调用Paddle工具包,构造一个二维卷积层,它包含一个的形状为[3,3]的卷积核
conv2d = paddle.nn.Conv2D(1, 1, kernel_size=(3, 3))
w_start = conv2d.weight.reshape((3,3)).numpy()

# 卷积层使用四维张量进行输入和输出(批量大小、通道、高度、宽度)
# 其中批量大小和通道数都为1
X = X.reshape((1, 1, 9, 9))  # 输入
Y = Y1.reshape((1, 1, 7, 7)) # 输出
lr = 4e-3                    # 学习率

for i in range(100):
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.clear_gradients()
    l.sum().backward()
    
    with paddle.no_grad():   # 迭代卷积核
        conv2d.weight[:] -= lr * conv2d.weight.grad
    if (i + 1) % 10 == 0:
        print(f'epoch {i+1}, loss {l.sum().item():.5f}')
epoch 10, loss 0.90101
epoch 20, loss 0.07812
epoch 30, loss 0.00799
epoch 40, loss 0.00082
epoch 50, loss 0.00008
epoch 60, loss 0.00001
epoch 70, loss 0.00000
epoch 80, loss 0.00000
epoch 90, loss 0.00000
epoch 100, loss 0.00000
  • 输出手工定义和学习获得的卷积核
In [15]:
w_learned = conv2d.weight.reshape((3,3))
print('W_GT={} \n w_start={} \n w_end={}'.format(W1.numpy(), w_start, w_learned.numpy()))
W_GT=[[ 1  0 -1]
 [ 1  0 -1]
 [ 1  0 -1]] 
 w_start=[[ 0.3962333  -0.48529047 -0.05017222]
 [ 0.35991043  0.00333536  0.31979078]
 [ 0.44011578  0.1082949   0.48617285]] 
 w_end=[[ 0.997482   -0.36074042 -1.302101  ]
 [ 0.961159    0.12788537 -0.9321376 ]
 [ 1.0413648   0.23284493 -0.7657556 ]]
  • 输出边缘检测的可视化结果
In [16]:
import cv2
import matplotlib.pyplot as plt
image = cv2.imread('../../Images/Materials/chapter04Datasets/chapter04008AugmentationExampleRIO.jpg', 0)
image = cv2.resize(image, (200,100))
img = paddle.to_tensor(image, dtype='float32')
out_hand_crafted = common.cross_correlation(img, W1)
out_learned = common.cross_correlation(img, conv2d.weight.reshape((3,3)))

plt.figure(figsize=(18,6))
ax = plt.subplot(1,3,1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
ax.set_title('Original Image', fontsize=18)
ax = plt.subplot(1,3,2)
plt.imshow(out_hand_crafted)
ax.set_title('The Hand-crafted Kernel', fontsize=18)
ax = plt.subplot(1,3,3)
plt.imshow(out_learned)
ax.set_title('The Learned Kernel', fontsize=18)
Out[16]:
Text(0.5, 1.0, 'The Learned Kernel')