深度学习实验代码-实验十-卷积神经网络(1)
·
一、自定义卷积核算子
import torch
import torch.nn as nn
class Conv2D(nn.Module):
def __init__(self, kernel_size, weight_value=None):
super(Conv2D, self).__init__()
# 根据传入的kernel_size初始化权重
if weight_value is None:
weight_value = torch.randn(kernel_size, kernel_size) # 随机初始化权重
self.weight = nn.Parameter(weight_value)
def forward(self, X):
"""
输入:
- X:输入矩阵,shape=[B, M, N],B为样本数量
输出:
- output:输出矩阵
"""
u, v = self.weight.shape # 获取卷积核长和宽
output = torch.zeros(X.shape[0], X.shape[1] - u + 1, X.shape[2] - v + 1) # 计算输出矩阵大小
for i in range(output.shape[1]):
for j in range(output.shape[2]):
# 执行卷积操作
output[:, i, j] = torch.sum(X[:, i:i + u, j:j + v] * self.weight, dim=[1, 2])
return output
# 使用示例
torch.manual_seed(100)
inputs = torch.tensor([[[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]]])
kernel_size = 2
conv2d = Conv2D(kernel_size=kernel_size, weight_value=torch.tensor([[0., 1.], [2., 3.]]))
outputs = conv2d(inputs)
print("input: \n{}, \noutput: \n{}".format(inputs, outputs))
二、自定义带步长与填充的卷积核算子
import torch
import torch.nn as nn
class Conv2D(nn.Module):
def __init__(self, kernel_size, stride, padding, weight_value=None):
super(Conv2D, self).__init__()
# 动态创建卷积核并初始化权重
if weight_value is None:
weight_value = torch.ones(kernel_size, kernel_size) # 默认初始化为1.0
self.weight = nn.Parameter(weight_value)
# 步长和填充
self.stride = stride
self.padding = padding
def forward(self, X):
# 零填充
if self.padding > 0:
new_X = torch.zeros(X.shape[0], X.shape[1] + 2 * self.padding, X.shape[2] + 2 * self.padding)
new_X[:, self.padding:X.shape[1] + self.padding, self.padding:X.shape[2] + self.padding] = X
else:
new_X = X
# 获取输出张量的维度大小
u, v = self.weight.shape
output_w = (new_X.shape[1] - u) // self.stride + 1
output_h = (new_X.shape[2] - v) // self.stride + 1
output = torch.zeros(X.shape[0], output_w, output_h)
# 进行带步长的卷积操作
for i in range(0, output.shape[1]):
for j in range(0, output.shape[2]):
output[:, i, j] = torch.sum(
new_X[:, i * self.stride:i * self.stride + u, j * self.stride:j * self.stride + v] * self.weight,
dim=[1, 2]
)
return output
# 输入张量,加上批次维度
inputs = torch.tensor([[1., 2., 3., 4.],
[5., 6., 7., 8.],
[9., 10., 11., 12.],
[13., 14., 15., 16.]]).unsqueeze(0) # 添加批次维度
# 创建 Conv2D 实例并应用
conv2d = Conv2D(kernel_size=2, padding=0, stride=2, weight_value=torch.tensor([[0., 1.],
[2., 3.]]))
outputs = conv2d(inputs)
print("input:\n {}, \noutput: \n{}".format(inputs, outputs))
三、编程实现图像边缘检测
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
import torch
import torch.nn as nn
# 设置中文字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
class Conv2D(nn.Module):
def __init__(self, kernel_size, stride, padding, weight_value=None):
super(Conv2D, self).__init__()
# 动态创建卷积核并初始化权重
if weight_value is None:
weight_value = torch.ones(kernel_size, kernel_size) # 默认初始化为1.0
self.weight = nn.Parameter(weight_value)
# 步长和填充
self.stride = stride
self.padding = padding
def forward(self, X):
# 零填充
if self.padding > 0:
new_X = torch.zeros(X.shape[0], X.shape[1] + 2 * self.padding, X.shape[2] + 2 * self.padding)
new_X[:, self.padding:X.shape[1] + self.padding, self.padding:X.shape[2] + self.padding] = X
else:
new_X = X
# 获取输出张量的维度大小
u, v = self.weight.shape
output_w = (new_X.shape[1] - u) // self.stride + 1
output_h = (new_X.shape[2] - v) // self.stride + 1
output = torch.zeros(X.shape[0], output_w, output_h)
# 进行带步长的卷积操作
for i in range(0, output.shape[1]):
for j in range(0, output.shape[2]):
output[:, i, j] = torch.sum(
new_X[:, i * self.stride:i * self.stride + u, j * self.stride:j * self.stride + v] * self.weight,
dim=[1, 2]
)
return output
image = Image.open('1.jpg').resize((256, 256)).convert('L') # 转换为灰度图像
X = np.array(image, dtype='float32')
X_tensor = torch.from_numpy(X).unsqueeze(0)
conv2d = Conv2D(kernel_size=3, padding=0, stride=1, weight_value=torch.tensor([[-1., -1., -1.],
[-1., 8., -1],
[-1., -1., -1.]]))
# 进行卷积
Conv_img1 = conv2d(X_tensor)
Conv_img1 = torch.clamp(Conv_img1, 0., 255.) # 解决锐化图像发灰问题
# 将卷积结果从计算图中分离出来,并转为NumPy数组
Conv_img1 = Conv_img1.detach().numpy().squeeze()
# 可视化原始图像和卷积后的特征图
plt.figure(figsize=(10, 5))
# 显示原始图像
plt.subplot(1, 2, 1)
plt.title("原始图像")
plt.imshow(X, cmap='gray') # 使用灰度色图显示
# 显示卷积后的特征图
plt.subplot(1, 2, 2)
plt.title("边缘检测滤波")
plt.imshow(Conv_img1, cmap='gray') # 使用灰度色图显示
plt.show()
这里附上实验中使用的"1.jpg"

四、自定义卷积层算子与汇聚层算子
import torch
import torch.nn as nn
class Pool2D(nn.Module):
def __init__(self, size=(2, 2), mode='max', stride=1):
super(Pool2D, self).__init__()
self.mode = mode
self.h, self.w = size
self.stride = stride
def forward(self, x):
# 计算输出大小
output_w = (x.shape[2] - self.w) // self.stride + 1
output_h = (x.shape[3] - self.h) // self.stride + 1
output = torch.zeros([x.shape[0], x.shape[1], output_w, output_h])
# 进行池化操作
for i in range(output.shape[2]):
for j in range(output.shape[3]):
# 最大池化
if self.mode == 'max':
window = x[:, :, self.stride * i:self.stride * i + self.h, self.stride * j:self.stride * j + self.w]
# 最大池化沿着高度和宽度
max_values, _ = torch.max(window, dim=2) # 沿高度方向
max_values, _ = torch.max(max_values, dim=2) # 沿宽度方向
output[:, :, i, j] = max_values
# 平均池化
elif self.mode == 'avg':
window = x[:, :, self.stride * i:self.stride * i + self.h, self.stride * j:self.stride * j + self.w]
# 平均池化沿着高度和宽度
avg_values = torch.mean(window, dim=[2, 3])
output[:, :, i, j] = avg_values
return output
# 输入张量:[batch_size, in_channels, height, width]
inputs = torch.tensor([[[[1., 2., 3., 4.],
[5., 6., 7., 8.],
[9., 10., 11., 12.],
[13., 14., 15., 16.]]]], dtype=torch.float32)
# 创建池化层并计算输出
pool2d = Pool2D(stride=2)
outputs = pool2d(inputs)
print("input shape:", inputs.shape)
print("output shape:", outputs.shape)
# 比较与PyTorch原生MaxPool2d运算结果
maxpool2d_pytorch = nn.MaxPool2d(kernel_size=(2, 2), stride=2)
outputs_pytorch_max = maxpool2d_pytorch(inputs)
# 自定义MaxPool2D运算结果
print('Custom MaxPool2D outputs:', outputs)
# PyTorch MaxPool2d API运算结果
print('PyTorch MaxPool2d outputs:', outputs_pytorch_max)
# 比较与PyTorch原生AvgPool2d运算结果
avgpool2d_pytorch = nn.AvgPool2d(kernel_size=(2, 2), stride=2)
outputs_pytorch_avg = avgpool2d_pytorch(inputs)
# 自定义AvgPool2D运算结果
pool2d_avg = Pool2D(mode='avg', stride=2)
outputs_avg = pool2d_avg(inputs)
# 自定义AvgPool2D运算结果
print('Custom AvgPool2D outputs:', outputs_avg)
# PyTorch AvgPool2d API运算结果
print('PyTorch AvgPool2d outputs:', outputs_pytorch_avg)
五、预定义算子与自定义算子的对比
Pytorch版本
import torch
import torch.nn as nn
# 输入张量 (1, 3, 5, 5) 表示 [batch_size, in_channels, height, width]
inputs = torch.tensor([[[[0, 1, 1, 0, 2],
[2, 2, 2, 2, 1],
[1, 0, 0, 2, 0],
[0, 1, 1, 0, 0],
[1, 2, 0, 0, 2]],
[[1, 0, 2, 2, 0],
[0, 0, 0, 2, 0],
[1, 2, 1, 2, 1],
[1, 0, 0, 0, 0],
[1, 2, 1, 1, 1]],
[[2, 1, 2, 0, 0],
[1, 0, 0, 1, 0],
[0, 2, 1, 0, 1],
[0, 1, 2, 2, 2],
[2, 1, 0, 0, 1]]]], dtype=torch.float32)
# 定义两个卷积核和偏置
filters = torch.tensor([[[[-1, 1, 0],
[0, 1, 0],
[0, 1, 1]],
[[-1, -1, 0],
[0, 0, 0],
[0, -1, 0]],
[[0, 0, -1],
[0, 1, 0],
[1, -1, -1]]],
[[[1, 1, -1],
[-1, -1, 1],
[0, -1, 1]],
[[0, 1, 0],
[-1, 0, -1],
[-1, 1, 0]],
[[-1, 0, 0],
[-1, 0, 1],
[-1, 0, 0]]]], dtype=torch.float32)
# 偏置
bias = torch.tensor([1, 0], dtype=torch.float32)
# 创建卷积层
conv2d = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, stride=2, padding=1, bias=True)
# 将权重和偏置初始化为自定义的值
with torch.no_grad():
conv2d.weight = nn.Parameter(filters)
conv2d.bias = nn.Parameter(bias)
# 进行卷积运算
outputs = conv2d(inputs)
# 打印输出
print("Conv2D outputs:\n", outputs)
自定义版本
import torch
import torch.nn as nn
class Conv2D(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
super(Conv2D, self).__init__()
# 初始化卷积核权重和偏置
self.weight = nn.Parameter(torch.ones(out_channels, in_channels, kernel_size, kernel_size))
self.bias = nn.Parameter(torch.zeros(out_channels, 1))
self.stride = stride
self.padding = padding
self.in_channels = in_channels
self.out_channels = out_channels
# 基础卷积运算
def single_forward(self, X, weight):
# 零填充
new_X = torch.zeros([X.shape[0], X.shape[1] + 2 * self.padding, X.shape[2] + 2 * self.padding])
new_X[:, self.padding:X.shape[1] + self.padding, self.padding:X.shape[2] + self.padding] = X
u, v = weight.shape
output_w = (new_X.shape[1] - u) // self.stride + 1
output_h = (new_X.shape[2] - v) // self.stride + 1
output = torch.zeros([X.shape[0], output_w, output_h])
for i in range(output.shape[1]):
for j in range(output.shape[2]):
output[:, i, j] = torch.sum(
new_X[:, i * self.stride:i * self.stride + u, j * self.stride:j * self.stride + v] * weight,
dim=[1, 2])
return output
def forward(self, inputs):
"""
输入:
- inputs: 输入矩阵,形状为 [B, D, M, N]
- weights: P组二维卷积核,形状为 [P, D, U, V]
- bias: P个偏置,形状为 [P, 1]
"""
feature_maps = []
# 进行多次多输入通道卷积运算
for p in range(self.out_channels):
multi_outs = []
for i in range(self.in_channels):
single = self.single_forward(inputs[:, i, :, :], self.weight[p, i, :, :])
multi_outs.append(single)
feature_map = torch.sum(torch.stack(multi_outs), dim=0) + self.bias[p]
feature_maps.append(feature_map)
# 将所有特征图堆叠起来
out = torch.stack(feature_maps, dim=1)
return out
# 定义输入矩阵 (1, 3, 5, 5),表示 [batch_size, in_channels, height, width]
inputs = torch.tensor([[[[0, 1, 1, 0, 2],
[2, 2, 2, 2, 1],
[1, 0, 0, 2, 0],
[0, 1, 1, 0, 0],
[1, 2, 0, 0, 2]],
[[1, 0, 2, 2, 0],
[0, 0, 0, 2, 0],
[1, 2, 1, 2, 1],
[1, 0, 0, 0, 0],
[1, 2, 1, 1, 1]],
[[2, 1, 2, 0, 0],
[1, 0, 0, 1, 0],
[0, 2, 1, 0, 1],
[0, 1, 2, 2, 2],
[2, 1, 0, 0, 1]]]], dtype=torch.float32)
# 定义卷积核和偏置
filter_w0 = torch.tensor([[[[-1, 1, 0],
[0, 1, 0],
[0, 1, 1]],
[[-1, -1, 0],
[0, 0, 0],
[0, -1, 0]],
[[0, 0, -1],
[0, 1, 0],
[1, -1, -1]]]], dtype=torch.float32)
filter_w1 = torch.tensor([[[[1, 1, -1],
[-1, -1, 1],
[0, -1, 1]],
[[0, 1, 0],
[-1, 0, -1],
[-1, 1, 0]],
[[-1, 0, 0],
[-1, 0, 1],
[-1, 0, 0]]]], dtype=torch.float32)
# 偏置
bias = torch.tensor([1, 0], dtype=torch.float32)
# 初始化卷积层
conv2d = Conv2D(in_channels=3, out_channels=2, kernel_size=3, stride=2, padding=1)
with torch.no_grad():
conv2d.weight[0] = filter_w0
conv2d.weight[1] = filter_w1
conv2d.bias[:, 0] = bias
# 计算输出
outputs = conv2d(inputs)
# 打印输出
print("Conv2D outputs:\n", outputs)
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)