8.2.4 自编码器去噪
8.2.4 自编码器去噪
自编码器(auto encoders)是深度学习中常见的一种模型。人类在理解复杂事物的时候,总是先总结初级的特征,然后从初级特征中总结出高级的特征。如图8-23所示,以识别手写数字为例,通过学习总结,发现可以把手写数字表示为几个非常简单的子图案的组合。
图8-23 将数字图案拆分成几个小图案的组合
对大量黑白风景照片提取16x16的图像碎片,分析研究后发现几乎所有的图像碎片都可以由64种正交的边组合得到。声音也存在相同的情况,大量未标注的音频中可以得到20种基本结构,绝大多数声音都可以由这些基本的结构线性组合得到。这就是特征的稀疏表达,通过少量的基本特征组合、拼装得到更高层抽象的特征。自编码器模型正好可以用于自动化地完成这种特征提取和表达的过程,而且整个过程是无监督的。基本的自编码器模型是一个简单的三层神经网络结构,如图8-24所示,由一个输入层、一个隐藏层和一个输出层组成,其中输出层和输入层具有相同的维数。
自编码器如图8-25所示,输入层和输出层分别代表神经网络的输入层和输出层,隐藏层承担编码器和解码器的工作,编码的过程就是从高维度的输入层转化到低维度的隐藏层的过程,反之,解码过程就是低维度的隐藏层到高维度的输出层的转化过程,可见自编码器是个有损转化的过程,通过对比输入和输出的差别来定义损失函数。训练的过程不需要对数据进行标记,整个过程就是不断求解损失函数最小化的过程,这也是自编码器名字的由来。
图8-24 自编码器模型神经网络结构
图8-25 自编码器原理图
自编码器通过学习数据集自身的特征,在一定程度上能过滤掉叠加到原始数据上的不规则的噪音,因此自编码器也通常被用于去噪。我们正是利用了自编码器去噪这一特性,来去除对抗样本中误导模型识别的噪音。如图8-26所示,在一个典型的去噪自编码器中,输入的是原始图像加上随机生成的噪声,通常随机噪声使用高斯噪声,然后经过解码器和编码器处理后,获得还原后的图像,通过计算原始图像和还原图像的损失函数,优化器调整解码器和编码器的参数,让损失函数的值趋向最小。经过若干轮迭代训练后,去噪自编码器可以从输入中去除噪音,让还原的图像尽可能接近原始图像。
下面我们结合实际的例子来介绍如何使用去噪自编码器抵御对抗样本攻击,对应的代码路径为:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/8-
case5.ipynb
首先我们需要定义去噪自编码器,数据集依然使用mnist,对应的编码器使用cnn结构,第一层使用64个3x3的卷积去处理,激活函数是relu;第二层是池化层,使用最大值池化,池化的大小为2x2;第三层使用128个3x3的卷积去处理,激活函数是relu;第四层是池化层,使用最大值池化,池化的大小为2x2。
self.encoder = nn.sequential(
nn.conv2d(1,64,kernel_size=3,stride=1,padding=1),
nn.relu(),
nn.maxpool2d(kernel_size=2,stride=2),
nn.conv2d(64,128,kernel_size=3,stride=1,padding=1),
nn.relu(),
nn.maxpool2d(kernel_size=2,stride=2),
)
图8-26 典型的去噪自编码器结构
编码器的结构与解码器十分类似,基本就是逆置的解码器,第一层是升采样层,放大系数为2;第二层是卷积层,输入的通道数为128,输出的通道数为64,卷积核大小为3x3,激活函数为relu;第三层是升采样层,放大系数为2;第四层是卷积层,输入的通道数为64,输出的通道数为1,卷积核大小为3x3。
self.decoder = nn.sequential(
nn.upsample(scale_factor=2,mode="nearest"),
nn.conv2d(128,64,kernel_size=3,stride=1,padding=1),
nn.relu(),
nn.upsample(scale_factor=2,mode="nearest"),
nn.conv2d(64,1,kernel_size=3,stride=1,padding=1),
)
整个去噪自编码器的前向传递定义如下:
def forward(self, x):
output = self.encoder(x)
output = self.decoder(output)
return output
实例化去噪自编码器,损失函数使用mseloss,即均方差误差,优化器使用adam。
autoencoder = autoencoder().to(device)
optimizer = torch.optim.adam(autoencoder.parameters(), lr=0.01)
loss_func = nn.mseloss()
加载mnist的训练数据,批处理大小为128,随机打散顺序。
train_data=datasets.mnist('../advbox/tutorials/mnist-pytorch/data',
train=true, download=true, transform=transforms.compose([
transforms.totensor(),
]))
train_loader = torch.utils.data.dataloader(
dataset=train_data,
batch_size=128, shuffle=true)
迭代训练,在原始数据inputs上叠加高斯噪声,噪声的标准差为0.1,均值为0,并对超过(0.0,1.0)范围的数据进行截断。
#迭代训练10轮
for epoch in range(10):
for i, data in enumerate(train_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
#增加噪声
inputs_noise=inputs+0.1*torch.randn(inputs.shape).to(device)
inputs_noise=torch.clamp(inputs_noise,0.0,1.0)
计算解码器的输出和原始数据之间的损失值,通过优化器调整去噪自编码器的各层参数。
output = autoencoder(inputs_noise)
loss = loss_func(output, inputs)
optimizer.zero_grad()
loss.backward()
optimizer.step()
下面我们验证去噪自编码器对正常图片识别的影响。使用的是预训练的mnist识别模型,数据集是mnist的测试集。
#使用mnist测试数据集随机挑选total_num个测试数据
# pytorch下的mnist数据集默认已归一化
test_loader = torch.utils.data.dataloader(
datasets.mnist('../advbox/tutorials/mnist-pytorch/data', train=false,
download=true, transform=transforms.compose([
transforms.totensor(),
])),
batch_size=1, shuffle=true)
加载预训练的模型,并设置为预测模式。定义全局计数器,包括样本总数,原始图片正常识别的个数,经过去噪后的图片正确识别的个数。
# 网络初始化
model = net().to(device)
# 加载模型
model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))
# 设置为预测模式
model.eval()
#统计总数
total_count = 0
#去噪前正确识别个数
pre_count=0
#去噪后正确识别个数
decoded_count = 0
遍历测试数据集,分别统计原始图片正常识别的个数,经过去噪后的图片正确识别的个数。
for i, data in enumerate(test_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
total_count += 1
#去噪前
pre_label=np.argmax(model(inputs).data.cpu().numpy())
if pre_label == labels[0]:
pre_count+=1
#使用自编码器去噪
output = autoencoder(inputs)
output=output.view(1,1,28,28)
decoded_label=np.argmax(model(output).data.cpu().numpy())
if decoded_label == labels[0]:
decoded_count+=1
随机选择1000个测试数据,原始图片的识别率为99.1%,去噪后的识别率为98.2%。
[test_dataset]: pre_count=991, total_count=1000, pre_count_rate=0.991000
decoded_count=982 decoded_count_rate=0.982000
最后使用advbox的fgsm对mnist的测试数据集进行无定向攻击,fgsm的攻击步长为0.01。
# advbox示例
m = pytorchmodel(
model, loss_func,(0, 1),
channel_axis=1)
#实例化fgsm
attack = fgsm(m)
#设置攻击步长为0.1
attack_config = {"epsilons": 0.01}
# 使用测试数据生成对抗样本
total_count = 0
# 去噪前的攻击成功个数
fooling_count = 0
# 去噪后的攻击成功个数
decoded_fooling_count = 0
#记录原始数据经过自编码器去噪后可以正常识别的个数
decoded_count = 0
随机选择1000个测试数据,fgsm无定向攻击原始数据的成功率为100%,经过去噪后成功率下降到11.6%。可以看出,在本例中去噪自编码器对原始图片的识别影响不大,但是可以有效抵御对抗样本的攻击。
[test_dataset]: fooling_count=1000, total_count=1000, fooling_rate=1.000000
decoded_fooling_count=116 decoded_fooling_count_rate=0.116000
fgsm attack done
shixiang gu等人在论文“towards deep neural network architectures robust to adversarial examples”中进行了更加深入的研究,并提出了针对去噪自编码器的一种改进算法contractive autoencoder(cae),即收缩自编码器,有兴趣的读者可以阅读该论文进一步了解。
自编码器(auto encoders)是深度学习中常见的一种模型。人类在理解复杂事物的时候,总是先总结初级的特征,然后从初级特征中总结出高级的特征。如图8-23所示,以识别手写数字为例,通过学习总结,发现可以把手写数字表示为几个非常简单的子图案的组合。
图8-23 将数字图案拆分成几个小图案的组合
对大量黑白风景照片提取16x16的图像碎片,分析研究后发现几乎所有的图像碎片都可以由64种正交的边组合得到。声音也存在相同的情况,大量未标注的音频中可以得到20种基本结构,绝大多数声音都可以由这些基本的结构线性组合得到。这就是特征的稀疏表达,通过少量的基本特征组合、拼装得到更高层抽象的特征。自编码器模型正好可以用于自动化地完成这种特征提取和表达的过程,而且整个过程是无监督的。基本的自编码器模型是一个简单的三层神经网络结构,如图8-24所示,由一个输入层、一个隐藏层和一个输出层组成,其中输出层和输入层具有相同的维数。
自编码器如图8-25所示,输入层和输出层分别代表神经网络的输入层和输出层,隐藏层承担编码器和解码器的工作,编码的过程就是从高维度的输入层转化到低维度的隐藏层的过程,反之,解码过程就是低维度的隐藏层到高维度的输出层的转化过程,可见自编码器是个有损转化的过程,通过对比输入和输出的差别来定义损失函数。训练的过程不需要对数据进行标记,整个过程就是不断求解损失函数最小化的过程,这也是自编码器名字的由来。
图8-24 自编码器模型神经网络结构
图8-25 自编码器原理图
自编码器通过学习数据集自身的特征,在一定程度上能过滤掉叠加到原始数据上的不规则的噪音,因此自编码器也通常被用于去噪。我们正是利用了自编码器去噪这一特性,来去除对抗样本中误导模型识别的噪音。如图8-26所示,在一个典型的去噪自编码器中,输入的是原始图像加上随机生成的噪声,通常随机噪声使用高斯噪声,然后经过解码器和编码器处理后,获得还原后的图像,通过计算原始图像和还原图像的损失函数,优化器调整解码器和编码器的参数,让损失函数的值趋向最小。经过若干轮迭代训练后,去噪自编码器可以从输入中去除噪音,让还原的图像尽可能接近原始图像。
下面我们结合实际的例子来介绍如何使用去噪自编码器抵御对抗样本攻击,对应的代码路径为:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/8-
case5.ipynb
首先我们需要定义去噪自编码器,数据集依然使用mnist,对应的编码器使用cnn结构,第一层使用64个3x3的卷积去处理,激活函数是relu;第二层是池化层,使用最大值池化,池化的大小为2x2;第三层使用128个3x3的卷积去处理,激活函数是relu;第四层是池化层,使用最大值池化,池化的大小为2x2。
self.encoder = nn.sequential(
nn.conv2d(1,64,kernel_size=3,stride=1,padding=1),
nn.relu(),
nn.maxpool2d(kernel_size=2,stride=2),
nn.conv2d(64,128,kernel_size=3,stride=1,padding=1),
nn.relu(),
nn.maxpool2d(kernel_size=2,stride=2),
)
图8-26 典型的去噪自编码器结构
编码器的结构与解码器十分类似,基本就是逆置的解码器,第一层是升采样层,放大系数为2;第二层是卷积层,输入的通道数为128,输出的通道数为64,卷积核大小为3x3,激活函数为relu;第三层是升采样层,放大系数为2;第四层是卷积层,输入的通道数为64,输出的通道数为1,卷积核大小为3x3。
self.decoder = nn.sequential(
nn.upsample(scale_factor=2,mode="nearest"),
nn.conv2d(128,64,kernel_size=3,stride=1,padding=1),
nn.relu(),
nn.upsample(scale_factor=2,mode="nearest"),
nn.conv2d(64,1,kernel_size=3,stride=1,padding=1),
)
整个去噪自编码器的前向传递定义如下:
def forward(self, x):
output = self.encoder(x)
output = self.decoder(output)
return output
实例化去噪自编码器,损失函数使用mseloss,即均方差误差,优化器使用adam。
autoencoder = autoencoder().to(device)
optimizer = torch.optim.adam(autoencoder.parameters(), lr=0.01)
loss_func = nn.mseloss()
加载mnist的训练数据,批处理大小为128,随机打散顺序。
train_data=datasets.mnist('../advbox/tutorials/mnist-pytorch/data',
train=true, download=true, transform=transforms.compose([
transforms.totensor(),
]))
train_loader = torch.utils.data.dataloader(
dataset=train_data,
batch_size=128, shuffle=true)
迭代训练,在原始数据inputs上叠加高斯噪声,噪声的标准差为0.1,均值为0,并对超过(0.0,1.0)范围的数据进行截断。
#迭代训练10轮
for epoch in range(10):
for i, data in enumerate(train_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
#增加噪声
inputs_noise=inputs+0.1*torch.randn(inputs.shape).to(device)
inputs_noise=torch.clamp(inputs_noise,0.0,1.0)
计算解码器的输出和原始数据之间的损失值,通过优化器调整去噪自编码器的各层参数。
output = autoencoder(inputs_noise)
loss = loss_func(output, inputs)
optimizer.zero_grad()
loss.backward()
optimizer.step()
下面我们验证去噪自编码器对正常图片识别的影响。使用的是预训练的mnist识别模型,数据集是mnist的测试集。
#使用mnist测试数据集随机挑选total_num个测试数据
# pytorch下的mnist数据集默认已归一化
test_loader = torch.utils.data.dataloader(
datasets.mnist('../advbox/tutorials/mnist-pytorch/data', train=false,
download=true, transform=transforms.compose([
transforms.totensor(),
])),
batch_size=1, shuffle=true)
加载预训练的模型,并设置为预测模式。定义全局计数器,包括样本总数,原始图片正常识别的个数,经过去噪后的图片正确识别的个数。
# 网络初始化
model = net().to(device)
# 加载模型
model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))
# 设置为预测模式
model.eval()
#统计总数
total_count = 0
#去噪前正确识别个数
pre_count=0
#去噪后正确识别个数
decoded_count = 0
遍历测试数据集,分别统计原始图片正常识别的个数,经过去噪后的图片正确识别的个数。
for i, data in enumerate(test_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
total_count += 1
#去噪前
pre_label=np.argmax(model(inputs).data.cpu().numpy())
if pre_label == labels[0]:
pre_count+=1
#使用自编码器去噪
output = autoencoder(inputs)
output=output.view(1,1,28,28)
decoded_label=np.argmax(model(output).data.cpu().numpy())
if decoded_label == labels[0]:
decoded_count+=1
随机选择1000个测试数据,原始图片的识别率为99.1%,去噪后的识别率为98.2%。
[test_dataset]: pre_count=991, total_count=1000, pre_count_rate=0.991000
decoded_count=982 decoded_count_rate=0.982000
最后使用advbox的fgsm对mnist的测试数据集进行无定向攻击,fgsm的攻击步长为0.01。
# advbox示例
m = pytorchmodel(
model, loss_func,(0, 1),
channel_axis=1)
#实例化fgsm
attack = fgsm(m)
#设置攻击步长为0.1
attack_config = {"epsilons": 0.01}
# 使用测试数据生成对抗样本
total_count = 0
# 去噪前的攻击成功个数
fooling_count = 0
# 去噪后的攻击成功个数
decoded_fooling_count = 0
#记录原始数据经过自编码器去噪后可以正常识别的个数
decoded_count = 0
随机选择1000个测试数据,fgsm无定向攻击原始数据的成功率为100%,经过去噪后成功率下降到11.6%。可以看出,在本例中去噪自编码器对原始图片的识别影响不大,但是可以有效抵御对抗样本的攻击。
[test_dataset]: fooling_count=1000, total_count=1000, fooling_rate=1.000000
decoded_fooling_count=116 decoded_fooling_count_rate=0.116000
fgsm attack done
shixiang gu等人在论文“towards deep neural network architectures robust to adversarial examples”中进行了更加深入的研究,并提出了针对去噪自编码器的一种改进算法contractive autoencoder(cae),即收缩自编码器,有兴趣的读者可以阅读该论文进一步了解。