卷积的意义及其应用
卷积的定义
我们将形如
∫
−
∞
∞
f
(
τ
)
g
(
x
−
τ
)
d
τ
\int^\infty_{-\infty} f(τ)g(x-τ)dτ
∫−∞∞f(τ)g(x−τ)dτ
的式子称之为f(x)与g(x)的卷积记为
h
(
x
)
=
(
f
∗
g
)
(
x
)
h(x) = (f * g)(x)
h(x)=(f∗g)(x)
我们可以将它转化成离散形式的式子,like:
∑
i
=
−
∞
∞
x
(
i
)
h
(
n
−
i
)
\sum^\infty_{i=-\infty}x(i)h(n-i)
i=−∞∑∞x(i)h(n−i)
这是一个很明显的将一个序列倒置,然后相乘的操作。我们就用如此的思想来引入我们的第一种引用——联合概率分布
卷积的应用——联合概率分布
我们想象两颗骰子,当我们掷出它们的时候对于每个骰子的六个面,有如下概率序列
[
1
6
,
1
6
,
1
6
,
1
6
,
1
6
,
1
6
]
[\frac{1}{6}, \frac{1}{6}, \frac{1}{6}, \frac{1}{6}, \frac{1}{6}, \frac{1}{6}]
[61,61,61,61,61,61]
它们的序号计数从1开始
两个骰子的点数概率序列我们分别命名为
P
(
x
)
,
Q
(
x
)
P(x), Q(x)
P(x),Q(x)
那么之前的离散型卷积我们可以选择一个位置写成如下形式,并且根据实际情况调节上下界,式子如
h
(
4
)
=
∑
i
=
1
3
P
(
i
)
Q
(
4
−
i
)
h(4) = \sum^{3}_{i=1}P(i)Q(4-i)
h(4)=i=1∑3P(i)Q(4−i)
这个描述的是两颗骰子投出4点的概率。
现在我们假设存在两个概率密度函数,定义域为x∈R,那么
∫
−
∞
∞
f
(
τ
)
g
(
x
−
τ
)
d
τ
\int^\infty_{-\infty} f(τ)g(x-τ)dτ
∫−∞∞f(τ)g(x−τ)dτ
这个式子就可以很清晰的表示他俩的联合分布的密度函数了。
我么可以用python代码实现它
import numpy as np
def dice_probability(num_dice):
# 构建骰子的点数概率分布
dice = np.ones(6) / 6
# 迭代卷积运算
result = dice
for _ in range(num_dice - 1):
result = np.convolve(result, dice)
return result
# 输入两个骰子进行卷积计算
num_dice = 2
probabilities = dice_probability(num_dice)
# 打印点数概率分布序列
for i, p in enumerate(probabilities, num_dice):
print(f"点数 {i}: 概率 {p:.4f}")
卷积的应用——信号处理
卷积还有另一个重要的作用,那就是信号处理。
我们假设存在一个线性信号系统,在其中有冲击函数f(t)和一个响应函数g(t)。
这个时候我们再去观察
∫
−
∞
∞
f
(
τ
)
g
(
x
−
τ
)
d
τ
\int^\infty_{-\infty} f(τ)g(x-τ)dτ
∫−∞∞f(τ)g(x−τ)dτ
我们不难知道其中的g(x-τ)其实是在x时,τ时接受的冲击的响应余留多少
我们知晓这个观点,那我们就可以写一个简单的python程序来计算这个冲击函数对应的真正的响应函数,(之前的g(t)其实是对于单位冲击而言的),代码如下
import numpy as np
import matplotlib.pyplot as plt
def continuous_convolution(signal_1, signal_2, dt):
conv = np.convolve(signal_1, signal_2) * dt
t = np.arange(0, (len(signal_1) + len(signal_2) - 1) * dt, dt)
return t, conv
# 定义输入信号的冲击函数和单位冲击响应函数
t_impulse = np.arange(-5, 5, 0.01)
impulse = np.zeros_like(t_impulse)
impulse[np.abs(t_impulse) < 0.001] = 1
t_response = np.arange(0, 10, 0.01)
response = np.exp(-t_response)
# 进行连续卷积计算
t_total, total_response = continuous_convolution(impulse, response, 0.01)
# 裁剪信号长度以匹配卷积结果
total_response = total_response[:len(t_total)]
# 创建一个包含三个子图的图形窗口
fig, axs = plt.subplots(1, 3, figsize=(12, 4))
# 绘制冲击函数的图像
axs[0].plot(t_impulse, impulse)
axs[0].set_xlabel('Time')
axs[0].set_ylabel('Amplitude')
axs[0].set_title('Impulse Function')
# 绘制单位冲击响应函数的图像
axs[1].plot(t_response, response)
axs[1].set_xlabel('Time')
axs[1].set_ylabel('Amplitude')
axs[1].set_title('Unit Impulse Response')
# 绘制总响应函数的图像
axs[2].plot(t_total[:-1], total_response)
axs[2].set_xlabel('Time')
axs[2].set_ylabel('Amplitude')
axs[2].set_title('Total Response')
# 调整子图之间的间距
plt.tight_layout()
# 展示图像
plt.show()
结果如下
卷积为我们忠实的展现了其在线性信号系统响应模拟上的能力
卷积的应用——图像处理
我们终于循序渐进的到了这一步,卷积——>卷积核。熟悉CNN网络的同学一定对这个词并不陌生,至于为什么把这个放到信号处理/概率后面来讲,因为卷积核实际上就是一个滤波器!!!!是对图像像素矩阵的滤波器!!!,每一次的更新迭代卷积核里的参数,是为了更方便的提取特征,换句话说,卷积核里的值,其实就是对每个像素点可能对这个问题提供贡献的概率的概率分布,每次迭代是迭代它的权重,我甚至可以将这个分布按我所想进行设计,比如说我要我可以将一个图片迅速的转化成速写
代码如下
import numpy as np
from PIL import Image
# 定义卷积操作函数
def convolve(image, kernel):
image_height, image_width = image.shape
kernel_height, kernel_width = kernel.shape
output = np.zeros((image_height, image_width))
# 滑动窗口进行卷积操作
for i in range(image_height - kernel_height + 1):
for j in range(image_width - kernel_width + 1):
window = image[i:i + kernel_height, j:j + kernel_width]
output[i, j] = np.sum(window * kernel)
return output
# 定义速写风格的卷积核
sketch_kernel = np.array([[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]])
# 从外部读取图像
image_path = "OIP-C.jpg" # 替换为你的图像路径
image = Image.open(image_path).convert("L") # 使用convert("L")将彩色图像转为灰度图像
# 将图像转为numpy数组
image_array = np.array(image)
# 执行卷积操作
sketch_image = convolve(image_array, sketch_kernel)
# 显示结果
image.show() # 显示原始图像
Image.fromarray(sketch_image).show() # 显示速写风格图像
下面的顺序为,原图,灰度化的原图,经卷积核的图
是不是要比灰度图更像铅笔画的呢,模糊的树丛也不模糊了?
这就是卷积核的作用,改变每个像素的权重参数,使其变成一个和卷积核的联合分布