其他
【强基固本】一网打尽CNN前向和反向 — 池化、padding、dropout
“强基固本,行稳致远”,科学研究离不开理论基础,人工智能学科更是需要数学、物理、神经科学等基础学科提供有力支撑,为了紧扣时代脉搏,我们推出“强基固本”专栏,讲解AI领域的基础知识,为你的科研学习提供助力,夯实理论基础,提升原始创新能力,敬请关注。
地址:https://zhuanlan.zhihu.com/p/372659296
01
class AvgPool2d:
def __init__(self, kernel_size=2, stride=2):
self.ksize = kernel_size
self.stride = stride
def forward(self, x):
B, C, H, W = input.shape
# reshape:[1,3,16,16] => [1,3,8,2,8,2]
x = x.reshape((B, C, H // self.ksize, self.ksize, W // self.ksize, self.ksize))
x = x.sum(axis=(3, 5)) # 对维度3和5求和, 获得[1,3,8,8]
x = x / self.ksize ** 2 # 除以sliding window的大小
return x
def backward(self, grad_output):
result = grad_output.repeat(self.ksize, axis=2).repeat(self.ksize, axis=3)/(self.ksize**2)
return result
pool = AvgPool2d(kernel_size=3, stride=3)
x = np.random.rand(1, 1, 6, 6)
y = pool.forward(x)
grad = np.random.randn(*y.shape)
x_grad = pool.backward(grad)
print(x)
print(y)
import torch
pool_torch = torch.nn.AvgPool2d(kernel_size=3,stride=3)
x = torch.from_numpy(x)
x.requires_grad = True # 如果要验证输入的梯度,需要将x.requires_grad置为True
x.retain_grad() # 保留中间过程
out = pool_torch.forward(x)
out.backward(torch.from_numpy(grad))
# 计算两种方式的误差
print(np.linalg.norm(y - out.detach().numpy()))
print(np.linalg.norm(x_grad - x.grad.detach().numpy()))
[[[[0.24787347 0.58462986 0.87057137 0.55470378 0.94212643 0.35796396]
[0.10316125 0.89490078 0.13275839 0.18731875 0.54144992 0.09144105]
[0.63152739 0.57627791 0.72482253 0.82538094 0.12523996 0.53229809]
[0.14704578 0.60045983 0.00351559 0.94632261 0.83554975 0.560136 ]
[0.41284019 0.8097822 0.25224494 0.62615051 0.16598724 0.75444111]
[0.81084053 0.65274578 0.62837219 0.52975354 0.00869069 0.86037101]]]]
[[[[0.52961366 0.46199143]
[0.47976078 0.58748916]]]]
1.5700924586837752e-16
0.0
02
class MaxPool2d:
def __init__(self, kernel_size=(2, 2), stride=2):
self.ksize = kernel_size
self.stride = stride
self.index = None # 这里记录的是非最大值的index
def forward(self, x):
N, C, H, W = x.shape
out = x.reshape(N, C, H // self.stride, self.stride, W // self.stride, self.stride)
out = out.max(axis=(3, 5))
self.index = out.repeat(self.ksize[0], axis=2).repeat(self.ksize[1], axis=3) != x
return out
def backward(self, grad_output):
result = grad_output.repeat(self.ksize[0], axis=2).repeat(self.ksize[1], axis=3)
result[self.index] = 0
return result
[[[[0.41082583 0.14887217 0.63720543 0.26269506 0.26689063 0.05299152]
[0.26417556 0.16590289 0.88674464 0.88322143 0.61245957 0.03046522]
[0.86418915 0.77299086 0.82797095 0.9822634 0.13698056 0.69444395]
[0.68113399 0.10417155 0.87924769 0.99260882 0.5379204 0.87514129]
[0.98859422 0.87058761 0.8880138 0.33134971 0.75367844 0.03569214]
[0.56870483 0.2410348 0.47228231 0.4095878 0.33161972 0.44368439]]]]
[[[[0.88674464 0.9822634 ]
[0.98859422 0.99260882]]]]
0.0
0.0
03
class Padding:
def __init__(self, padding_size=(1, 1, 1, 1), value=0):
self.padding_size = padding_size
self.value = value
def forward(self, x):
N, C, H, W = x.shape
pad = np.ones((N, C, H + self.padding_size[2] + self.padding_size[3], W + self.padding_size[0] + self.padding_size[1]))
pad *= self.value
pad[..., self.padding_size[2]:H + self.padding_size[2], self.padding_size[0]:H + self.padding_size[0]] = x
return pad
def backward(self, grad_output):
return grad_output[..., self.padding_size[2]:grad_output.shape[2] - self.padding_size[3],self.padding_size[0]:grad_output.shape[3] - self.padding_size[1]]
padding = Padding(padding_size=(1, 1, 1, 1), value=0.0)
x = np.random.randn(1,1,3,3)
y = padding.forward(x)
y_grad = np.random.randn(*y.shape)
x_grad = padding.backward(y_grad)
print(y)
import torch
padding_torch = torch.nn.ConstantPad2d(padding=(1,1,1,1), value=0.0)
x = torch.from_numpy(x)
x.requires_grad = True
x.retain_grad()
out = padding_torch(x)
out.backward(torch.from_numpy(y_grad))
print(np.linalg.norm(y-out.detach().numpy()))
print(np.linalg.norm(x_grad-x.grad.detach().numpy()))
[[[[ 0. 0. 0. 0. 0. ]
[ 0. 2.3513949 -0.18764089 0.53695311 0. ]
[ 0. -0.08604583 -0.15421848 -0.21025646 0. ]
[ 0. -0.31895118 0.35480772 -0.78156063 0. ]
[ 0. 0. 0. 0. 0. ]]]]
0.0
0.0
04
class Dropout():
def __init__(self, drop_rate=0.5, is_train=True):
self.drop_rate = drop_rate
self.is_train = is_train
self.fix_value = 1 - drop_rate # 修正期望,保证输出值的期望不变
self.save_mask = None
def forward(self, x):
if not self.is_train: # 当前为测试状态
return x
else: # 当前为训练状态
N, m = x.shape
self.save_mask = np.random.uniform(0, 1, m) > self.drop_rate # save_mask中为保留的神经元
return (x * self.save_mask) / self.fix_value
def backward(self, grad_output):
if not self.is_train:
return grad_output
else:
return eta * self.save_mask/self.fix_value
import torch
x = np.random.rand(2, 6)
pool_torch = torch.nn.Dropout(p=0.5)
x_t = torch.from_numpy(x)
x_t.requires_grad = True
y = pool_torch.forward(x_t)
print('y:', y)
x_t.retain_grad()
eta = torch.rand_like(y)
y.backward(eta)
print('x_grad:',x_t.grad)
pool = Dropout(drop_rate=0.5)
y = pool.forward(x)
print('y:', y)
z = pool.backward(eta.numpy())
print('x_grad:', z)
y: tensor([[0.0000, 0.3316, 1.8085, 0.0000, 0.0000, 1.6922],
[0.0000, 1.8447, 1.4564, 0.6675, 0.0000, 0.9850]], dtype=torch.float64,
grad_fn=<MulBackward0>)
x_grad: tensor([[0.0000, 0.6242, 0.3538, 0.0000, 0.0000, 1.2362],
[0.0000, 1.6603, 0.4200, 1.8347, 0.0000, 1.8959]], dtype=torch.float64)
y: [[0. 0.33158154 1.80852172 0. 0. 1.69216999]
[0. 1.8446859 1.45642869 0. 0. 0.98497914]]
x_grad: [[0. 0.62420836 0.35375397 0. 0. 1.23621819]
[0. 1.66027998 0.41999203 0. 0. 1.89585741]]
本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。
“强基固本”历史文章
神经网络、流形和拓扑
高维数据可视化:T-SNE
基础知识 | 对目标检测认识及理解
一步步用c++实现harris角点检测
深度学习与围棋:为围棋数据设计神经网络
最受欢迎的算法之一:反向传播训练
神经网络结构下理解Logistic Regression &TF框架下构造Logistic实现Mnist分类
深入探究MMD距离
机器学习常用评价指标总览
信息量、熵、相对熵(KL散度)、交叉熵
神经网络常用求导
深度学习算法收敛性证明之拓展SGD
看了这篇文章你还不懂SVM你就来打我
卷积神经网络(CNN)反向传播算法
更多强基固本专栏文章,
请点击文章底部“阅读原文”查看
分享、点赞、在看,给个三连击呗!