6.3 本地搜索攻击算法
6.3 本地搜索攻击算法
在实际使用中,单像素攻击对于比较简单的数据集有很好的攻击效果,比如mnist和cifar10,cifar10数据集如图6-2所示。这类数据集的图片大小普遍比较小,比如mnist形状为[28,28,1],cifar10形状为[32,32,3],针对一个像素点的修改可以对分类结果产生较大影响。但是当图像较大时,一个像素点的改变很难影响到分类结果。并且随着图像文件的增大,搜索空间也迅速增大,单像素攻击的效率也会快速下降。针对这一情况,有的单像素攻击在实现上允许同时修改一个以上的像素点,比如同时修改50个像素点,但是攻击效果并不明显。
本地搜索攻击算法(local search attack)改进了单像素攻击算法,论文《simple black-box adversarial perturbations for deep networks》重点介绍了该算法。单像素攻击算法没有很好地利用模型的反馈信息去优化扰动,很大程度上依赖随机选择像素和迭代调整像素点的值。本地搜索攻击算法的主要改进点就是根据模型的反馈信息去选择扰动的点,并随机选择对分类结果影响大的点周围的点,进一步进行选择。
首先用伪码描述新的扰动函数cyclic。
cyclic(r,b,x,y)
输入:图像i,r是扰动系数,并且r∈[0,2]
if ri(b,x,y)< lb then
return ri(b,x,y)+(ub - lb)
else if ri(b,x,y)> ub then
return ri(b,x,y)-(ub - lb)
else
return ri(b,x,y)
else if
图6-2 cifar10数据集
本地搜索攻击算法可以用伪码描述为:
locsearchadv(nn)
输入:图像i,分类标签为c(i)∈{1,…,c}, p和r是扰动系数,并且r∈[0,2]
正方形搜索空间的边长的一半为d,最大迭代次数为r,每次迭代选择的像素的个数为t
i=1
随机选择10%的像素,初始化集合(px,py)
while i ≤ r do
遍历(px,py)的像素,进行扰动pert(i,p,x,y),得到新图像集合
计算新图像集合中每个图像预测原分类标签的概率score(i)
选择概率最大的t个像素点,得到新的像素集合(px,py)
遍历(px,py),依次在原始图像上进行扰动cyclic(r,b,x,y),更新原始图像i
if c(i)?π(nn(ip),1) then
攻击成功
end if
遍历(px,py),以每个像素点作为中心,画出边长为2d的正方形,正方形范围内的点都纳入(px,py)
end while
目前advbox和foolbox均实现了单像素攻击算法,下面继续以advbox为例介绍本地搜索攻击算法的具体实现。
首先定义了标准化和逆标准化函数,为了处理方便,需要把数据标准化到[–0.5,0.5]之间。
def normalize(im):
im = im -(min_ + max_) / 2
im = im / (max_ - min_)
lb = -1 / 2
ub = 1 / 2
return im, lb, ub
def unnormalize(im):
im = im * (max_ - min_)
im = im + (min_ + max_) / 2
return im
adv_img, lb, ub = normalize(adv_img)
随机选择一部分像素点,总数不超过全部的10%,最大为128个。
def random_locations():
n = int(0.1 * h * w)
n = min(n, 128)
locations = np.random.permutation(h * w)[:n]
p_x = locations % w
p_y = locations // w
pxy = list(zip(p_x, p_y))
pxy = np.array(pxy)
return pxy
实现扰动函数cyclic。
def cyclic(r, ibxy):
result = r * ibxy
if result.any() < lb:
result = result + (ub - lb)
elif result.any() > ub:
result = result - (ub - lb)
result=result.clip(lb,ub)
return result
初始化图像以及(px,py)集合。
ii = adv_img
pxpy = random_locations()
从(px,py)集合中随机选择最多128个点,遍历每个点,针对每个点进行扰动并生成一个新的图像,得到一个新图像集合。对新图像集合中的每个图像进行计算,得到分类为原标签的概率值。
for try_time in range(r):
#重新排序,随机选择不超过128个点
pxpy = pxpy[np.random.permutation(len(pxpy))[:128]]
l = [pert(ii, p, x, y) for x, y in pxpy]
#批量返回预测结果,获取原始图像标签的概率
def score(its):
its = np.stack(its)
its = unnormalize(its)
scores=[ self.model.predict(unnormalize(ii))[original_
label] for it in its ]
return scores
选择影响力最大的t个点组成新的(px,py)集合,np.argsort返回的是升序排列,因此需要取倒数t个,这里非常容易弄错。
scores = score(l)
indices = np.argsort(scores)[:-t]
pxpy_star = pxpy[indices]
遍历(px,py)集合,同时在原始图片上进行扰动。
for x, y in pxpy_star:
#对每个颜色通道的[x,y]点进行扰动并截断,扰动算法即放大r倍
for b in range(channels):
location = [x, y]
location.insert(self.model.channel_axis(), b)
location = tuple(location)
ii[location] = cyclic(r, ii[location])
针对新生成的图片进行预测,如果分类标签发生变化即说明攻击成功,反之继续。更新(px,py)集合,以每个像素点作为中心,画出边长为2d的正方形,正方形范围内的点都纳入集合(px,py),所谓的“本地搜索”也就体现在这里。
pxpy = [ (x, y) for _a, _b in pxpy_star
for x in range(_a - d, _a + d + 1)
for y in range(_b - d, _b + d + 1)]
pxpy = [(x, y) for x, y in pxpy if 0 <= x < w and 0 <= y < h]
pxpy = list(set(pxpy))
pxpy = np.array(pxpy)
在实际使用中,单像素攻击对于比较简单的数据集有很好的攻击效果,比如mnist和cifar10,cifar10数据集如图6-2所示。这类数据集的图片大小普遍比较小,比如mnist形状为[28,28,1],cifar10形状为[32,32,3],针对一个像素点的修改可以对分类结果产生较大影响。但是当图像较大时,一个像素点的改变很难影响到分类结果。并且随着图像文件的增大,搜索空间也迅速增大,单像素攻击的效率也会快速下降。针对这一情况,有的单像素攻击在实现上允许同时修改一个以上的像素点,比如同时修改50个像素点,但是攻击效果并不明显。
本地搜索攻击算法(local search attack)改进了单像素攻击算法,论文《simple black-box adversarial perturbations for deep networks》重点介绍了该算法。单像素攻击算法没有很好地利用模型的反馈信息去优化扰动,很大程度上依赖随机选择像素和迭代调整像素点的值。本地搜索攻击算法的主要改进点就是根据模型的反馈信息去选择扰动的点,并随机选择对分类结果影响大的点周围的点,进一步进行选择。
首先用伪码描述新的扰动函数cyclic。
cyclic(r,b,x,y)
输入:图像i,r是扰动系数,并且r∈[0,2]
if ri(b,x,y)< lb then
return ri(b,x,y)+(ub - lb)
else if ri(b,x,y)> ub then
return ri(b,x,y)-(ub - lb)
else
return ri(b,x,y)
else if
图6-2 cifar10数据集
本地搜索攻击算法可以用伪码描述为:
locsearchadv(nn)
输入:图像i,分类标签为c(i)∈{1,…,c}, p和r是扰动系数,并且r∈[0,2]
正方形搜索空间的边长的一半为d,最大迭代次数为r,每次迭代选择的像素的个数为t
i=1
随机选择10%的像素,初始化集合(px,py)
while i ≤ r do
遍历(px,py)的像素,进行扰动pert(i,p,x,y),得到新图像集合
计算新图像集合中每个图像预测原分类标签的概率score(i)
选择概率最大的t个像素点,得到新的像素集合(px,py)
遍历(px,py),依次在原始图像上进行扰动cyclic(r,b,x,y),更新原始图像i
if c(i)?π(nn(ip),1) then
攻击成功
end if
遍历(px,py),以每个像素点作为中心,画出边长为2d的正方形,正方形范围内的点都纳入(px,py)
end while
目前advbox和foolbox均实现了单像素攻击算法,下面继续以advbox为例介绍本地搜索攻击算法的具体实现。
首先定义了标准化和逆标准化函数,为了处理方便,需要把数据标准化到[–0.5,0.5]之间。
def normalize(im):
im = im -(min_ + max_) / 2
im = im / (max_ - min_)
lb = -1 / 2
ub = 1 / 2
return im, lb, ub
def unnormalize(im):
im = im * (max_ - min_)
im = im + (min_ + max_) / 2
return im
adv_img, lb, ub = normalize(adv_img)
随机选择一部分像素点,总数不超过全部的10%,最大为128个。
def random_locations():
n = int(0.1 * h * w)
n = min(n, 128)
locations = np.random.permutation(h * w)[:n]
p_x = locations % w
p_y = locations // w
pxy = list(zip(p_x, p_y))
pxy = np.array(pxy)
return pxy
实现扰动函数cyclic。
def cyclic(r, ibxy):
result = r * ibxy
if result.any() < lb:
result = result + (ub - lb)
elif result.any() > ub:
result = result - (ub - lb)
result=result.clip(lb,ub)
return result
初始化图像以及(px,py)集合。
ii = adv_img
pxpy = random_locations()
从(px,py)集合中随机选择最多128个点,遍历每个点,针对每个点进行扰动并生成一个新的图像,得到一个新图像集合。对新图像集合中的每个图像进行计算,得到分类为原标签的概率值。
for try_time in range(r):
#重新排序,随机选择不超过128个点
pxpy = pxpy[np.random.permutation(len(pxpy))[:128]]
l = [pert(ii, p, x, y) for x, y in pxpy]
#批量返回预测结果,获取原始图像标签的概率
def score(its):
its = np.stack(its)
its = unnormalize(its)
scores=[ self.model.predict(unnormalize(ii))[original_
label] for it in its ]
return scores
选择影响力最大的t个点组成新的(px,py)集合,np.argsort返回的是升序排列,因此需要取倒数t个,这里非常容易弄错。
scores = score(l)
indices = np.argsort(scores)[:-t]
pxpy_star = pxpy[indices]
遍历(px,py)集合,同时在原始图片上进行扰动。
for x, y in pxpy_star:
#对每个颜色通道的[x,y]点进行扰动并截断,扰动算法即放大r倍
for b in range(channels):
location = [x, y]
location.insert(self.model.channel_axis(), b)
location = tuple(location)
ii[location] = cyclic(r, ii[location])
针对新生成的图片进行预测,如果分类标签发生变化即说明攻击成功,反之继续。更新(px,py)集合,以每个像素点作为中心,画出边长为2d的正方形,正方形范围内的点都纳入集合(px,py),所谓的“本地搜索”也就体现在这里。
pxpy = [ (x, y) for _a, _b in pxpy_star
for x in range(_a - d, _a + d + 1)
for y in range(_b - d, _b + d + 1)]
pxpy = [(x, y) for x, y in pxpy if 0 <= x < w and 0 <= y < h]
pxpy = list(set(pxpy))
pxpy = np.array(pxpy)