文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何用DL4J对人脸识别模型进行攻击

2023-06-19 12:29

关注

如何用DL4J对人脸识别模型进行攻击,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

一、前言

    下面就来介绍一下如何用DeepLearning4J对人脸识别模型进行FGSM攻击。主要包含两块内容。

    1、ML Attack的基本原理。

    2、结合天池人脸识别对抗比赛实例讲解攻击过程。

二、机器学习模型的攻击

    1、对ML攻击的过程

     对机器学习模型的攻击,一句话描述就是给input特征加入一些微小的噪声让模型识别错误,以图像识别为例。

    如何用DL4J对人脸识别模型进行攻击

    上图展示的就是原图加上噪声如何用DL4J对人脸识别模型进行攻击之后,让猫被识别为了狗,这就是攻击的过程。

    2、攻击的原理

    我们来回想一下,机器学习模型大部分时候是在对Loss Function求极小值,攻击这个Loss函数即可,固定住模型参数,反过来求解一个特征X,让Loss Function值越大越好。

    回想一下模型的训练过程,假设有一个图片x,模型的参数p,label为c1(假设c1表示猫的分类),我们定义一个损失函数:L = Loss(x,p,c1),其中x和c1都是固定的,通过调节参数p在训练集上求得L的最小值。

   (1)、无目标攻击

    无目标攻击就是没有定向目标,让模型分类错误即可,那么用公式描述为:argmax  Loss(y,p,c1),在p和c1固定的情况下求得一个y,使得Loss函数最大。

    例如,输入一张猫的图片,希望模型预测错误,无论模型预测为什么都可以,只要不是猫就行。

    如何用DL4J对人脸识别模型进行攻击

  (2)、有目标攻击

    有目标攻击是让模型将input识别为我们想要的对象,比方说,我们输入一张猫的图像,希望模型预测为狗的分类。设c2为狗的分类,那么用公式描述有目标攻击如下:

    argmin (-Loss(y,p,c1)+Loss(y,p,c2))

    在p、c1、c2固定的情况下,求解一个y使得上述函数最小,通俗一点就是说,输入一张猫的图片,希望模型预测为狗

    如何用DL4J对人脸识别模型进行攻击

    备注:其中y表示原图x加上了噪声,c1表示猫的分类,c2表示狗的分类。

    最后还有一个问题,加入噪声如果强度过大,原始图片就失真了,我们希望加入的杂讯很微小,不容易不察觉,我们需要做一个限定,定义函数d(x,y)<t,函数d表示两个图片x,y的距离,t表示一个微小的阈值。这样就限定了噪声的范围,不能过大。

    我们知道模型的结构,就可以进行攻击了,这个是白盒攻击,但是大部分时候,模型的结构我们无从得知,可以进行黑盒攻击。黑盒攻击就是攻击代理模型,比方说对vggnet的攻击在ResNet上同样管用。下面是一些数据,来证明黑盒攻击有用。(表格里的数字表示正确率)

如何用DL4J对人脸识别模型进行攻击

三、FGSM攻击

    论文地址:https://arxiv.org/abs/1412.6572

    求解一个y =如何用DL4J对人脸识别模型进行攻击

四、天池人脸识别对抗

    1、比赛地址:https://tianchi.aliyun.com/competition/entrance/231745/information

    2、比赛评分规则:

        为了保证扰动后人脸的视觉效果,本次比赛限制单个像素的扰动在[-25.5, 25.5]区间内,对于扰动超出该范围的提交结果,我们会在后台强制把图像扰动截断至[-25.5, 25.5]区间(使用numpy.clip函数)。所以请参赛选手控制提交的对抗样本与原图在单像素上的差异。

    对每个生成的对抗样本,后台会采用模型对该样本进行预测,并根据识别结果计算相应的扰动量,具体计算公式如下:

    如何用DL4J对人脸识别模型进行攻击

     其中 M表示后台模型预测结果, y表示样本 I的真实标签。如果防御算法对样本识别正确,此次攻击不成功,扰动量直接置为上限44.1673。该上限可由约束的最大扰动25.5计算得出。如果攻击成功,计算对抗样本 I^a 和原始样本I的 L2 距离,作为得分,得分越小越好。

    一句话描述规则就是改动越小,攻击成功率越高,成绩越好。

五、DeepLearning4j进行FGSM攻击

    1、解决dl4j对input求梯度问题

    我们攻击的代理模型同样是VggFace(为什么一直都是选vggface,确实dl4j只有vggface,哎,也没有其他选择),ComputationGraph中梯度反向传播完成Gradient对象就被回收了 ,这样设计的目的就是为了节省内存。在MultiLayerNetwork中可以通过org.deeplearning4j.nn.multilayer.MultiLayerNetwork#calculateGradients求Loss对input的偏导数,但我们怎么拿的ComputationGraph中Loss对input的偏导数呢?不着急,我们在源码中找答案,我们细看源码org.deeplearning4j.nn.graph.ComputationGraph#calcBackpropGradients中反向传播做了什么?

 try (MemoryWorkspace wsWorkingMem = workspaceMgr.notifyScopeEntered(ArrayType.BP_WORKING_MEM)) {                    pair = current.doBackward(truncatedBPTT, workspaceMgr);                    epsilons = pair.getSecond();                    //Validate workspace location for the activation gradients:                    //validateArrayWorkspaces(LayerWorkspaceMgr mgr, INDArray array, ArrayType arrayType, String vertexName, boolean isInputVertex, String op){                    for (INDArray epsilon : epsilons) {                        if (epsilon != null) {                            //May be null for EmbeddingLayer, etc                            validateArrayWorkspaces(workspaceMgr, epsilon, ArrayType.ACTIVATION_GRAD, vertexName, false, "Backprop");                        }                    }                }

    跟进org.deeplearning4j.nn.graph.vertex.GraphVertex#doBackward方法

public Pair<Gradient, INDArray[]> doBackward(boolean tbptt, LayerWorkspaceMgr workspaceMgr) {        if (!canDoBackward()) {            if(inputs == null || inputs[0] == null){                throw new IllegalStateException("Cannot do backward pass: inputs not set. Layer: \"" + vertexName                        + "\" (idx " + vertexIndex + "), numInputs: " + getNumInputArrays());            } else {                throw new IllegalStateException("Cannot do backward pass: all epsilons not set. Layer \"" + vertexName                        + "\" (idx " + vertexIndex + "), numInputs :" + getNumInputArrays() + "; numOutputs: "                        + getNumOutputConnections());            }        }        //Edge case: output layer - never did forward pass hence layer.setInput was never called...        if(!setLayerInput){            applyPreprocessorAndSetInput(workspaceMgr);        }        Pair<Gradient, INDArray> pair;        if (tbptt && layer instanceof RecurrentLayer) {            //Truncated BPTT for recurrent layers            pair = ((RecurrentLayer) layer).tbpttBackpropGradient(epsilon,                            graph.getConfiguration().getTbpttBackLength(), workspaceMgr);        } else {            //Normal backprop            pair = layer.backpropGradient(epsilon, workspaceMgr); //epsTotal may be null for OutputLayers        }        if (layerPreProcessor != null) {            INDArray eps = pair.getSecond();            eps = layerPreProcessor.backprop(eps, graph.batchSize(), workspaceMgr);            pair.setSecond(eps);        }        //Layers always have single activations input -> always have single epsilon output during backprop        return new Pair<>(pair.getFirst(), new INDArray[] {pair.getSecond()});    }

    里面有个org.deeplearning4j.nn.conf.InputPreProcessor#backprop将梯度回传给InputPreProcessor处理。于是我们就有思路了,我们只需要给第一层卷积层设置一个InputPreProcessor即可获取回传的梯度,注意LayerVertex的InputPreProcessor是final修饰的,那么怎么设置InputPreProcessor呢?这个难不倒Javaer,反射。

public class LayerVertex extends BaseGraphVertex {    private Layer layer;    private final InputPreProcessor layerPreProcessor;    private boolean setLayerInput;

    接下来先实现一个InputPreProcessor,把回传的梯度放在一个static变量里

public class Preprocessor implements InputPreProcessor {private static final long serialVersionUID = 1L;public static INDArray epsilon;@Overridepublic INDArray preProcess(INDArray input, int miniBatchSize, LayerWorkspaceMgr workspaceMgr) {return workspaceMgr.dup(ArrayType.ACTIVATIONS, input);}@Overridepublic InputType getOutputType(InputType inputType) {return inputType;}@Overridepublic Pair<INDArray, MaskState> feedForwardMaskArray(INDArray maskArray, MaskState currentMaskState,int minibatchSize) {return null;}@Overridepublic INDArray backprop(INDArray output, int miniBatchSize, LayerWorkspaceMgr workspaceMgr) {epsilon = output.detach();return workspaceMgr.dup(ArrayType.ACTIVATION_GRAD, output);}@Overridepublic InputPreProcessor clone() {// TODO Auto-generated method stubreturn null;}}

        接下来先dl4j transfer learning API加载vggface的模型,去掉全连接层,加上CnnLossLayer作为output,这里Loss函数用的COSINE_PROXIMITY(尝试过多种方法之后,发现cosine距离效果最好),然后反射给第一层卷积层加上InputPreProcessor,反射时调用Field的setAccessible(true)方法,开放private属性的访问权限(当然这是迫不得已的方法),请看下面代码。

ComputationGraph pretrained = (ComputationGraph) VGG16.builder().build().initPretrained(PretrainedType.VGGFACE);System.out.println(pretrained.summary());FineTuneConfiguration fineTuneConf = new FineTuneConfiguration.Builder().updater(new Sgd(0)).seed(123).build();ComputationGraph vgg16Transfer = new TransferLearning.GraphBuilder(pretrained).fineTuneConfiguration(fineTuneConf).removeVertexAndConnections("flatten").removeVertexAndConnections("fc6").removeVertexAndConnections("fc7").removeVertexAndConnections("fc8").addLayer("out", new CnnLossLayer.Builder(LossFunctions.LossFunction.COSINE_PROXIMITY).activation(Activation.IDENTITY).build(), "pool5").setOutputs("out").build();LayerVertex conv1_1 = (LayerVertex) vgg16Transfer.getVertex("conv1_1");Class<?> clz = conv1_1.getClass();Field nameField = clz.getDeclaredField("layerPreProcessor");nameField.setAccessible(true);nameField.set(conv1_1, new Preprocessor());System.out.println(vgg16Transfer.summary());

    到此为止,Loss对input的偏导数就可以通过Preprocessor.epsilon获取到了,这个问题解决了,就可以进行攻击了。最终代理模型的结构如下:

================================================================================================VertexName (VertexType)      nIn,nOut   TotalParams   ParamsShape                  Vertex Inputs================================================================================================input_1 (InputVertex)        -,-        -             -                            -            conv1_1 (ConvolutionLayer)   3,64       1,792         W:{64,3,3,3}, b:{1,64}       [input_1]    conv1_2 (ConvolutionLayer)   64,64      36,928        W:{64,64,3,3}, b:{1,64}      [conv1_1]    pool1 (SubsamplingLayer)     -,-        0             -                            [conv1_2]    conv2_1 (ConvolutionLayer)   64,128     73,856        W:{128,64,3,3}, b:{1,128}    [pool1]      conv2_2 (ConvolutionLayer)   128,128    147,584       W:{128,128,3,3}, b:{1,128}   [conv2_1]    pool2 (SubsamplingLayer)     -,-        0             -                            [conv2_2]    conv3_1 (ConvolutionLayer)   128,256    295,168       W:{256,128,3,3}, b:{1,256}   [pool2]      conv3_2 (ConvolutionLayer)   256,256    590,080       W:{256,256,3,3}, b:{1,256}   [conv3_1]    conv3_3 (ConvolutionLayer)   256,256    590,080       W:{256,256,3,3}, b:{1,256}   [conv3_2]    pool3 (SubsamplingLayer)     -,-        0             -                            [conv3_3]    conv4_1 (ConvolutionLayer)   256,512    1,180,160     W:{512,256,3,3}, b:{1,512}   [pool3]      conv4_2 (ConvolutionLayer)   512,512    2,359,808     W:{512,512,3,3}, b:{1,512}   [conv4_1]    conv4_3 (ConvolutionLayer)   512,512    2,359,808     W:{512,512,3,3}, b:{1,512}   [conv4_2]    pool4 (SubsamplingLayer)     -,-        0             -                            [conv4_3]    conv5_1 (ConvolutionLayer)   512,512    2,359,808     W:{512,512,3,3}, b:{1,512}   [pool4]      conv5_2 (ConvolutionLayer)   512,512    2,359,808     W:{512,512,3,3}, b:{1,512}   [conv5_1]    conv5_3 (ConvolutionLayer)   512,512    2,359,808     W:{512,512,3,3}, b:{1,512}   [conv5_2]    pool5 (SubsamplingLayer)     -,-        0             -                            [conv5_3]    out (CnnLossLayer)           -,-        0             -                            [pool5]      ------------------------------------------------------------------------------------------------            Total Parameters:  14,714,688        Trainable Parameters:  14,714,688           Frozen Parameters:  0================================================================================================

    2、生成Label张量

    下面把需要攻击的目标图片下载下来,我放在D盘了,目标图片如下。

    如何用DL4J对人脸识别模型进行攻击

    下面用vggFace读取所有图片,把图片转化为张量,我们只需要获取最后一个池化层的输出就可以了。请看下面代码

NativeImageLoader loader = new NativeImageLoader(224, 224, 3, new ResizeImageTransform(224, 224));File file = new File("D:/securityAI_round1_images/images");ImageLoader imageLoader = new ImageLoader(112, 112, 3);List<File> list = new ArrayList<>();for (File f : file.listFiles()) {list.add(f);}Map<Integer, INDArray> labelMap = new HashMap<>();for (int i = 0; i < list.size(); i++) {vgg16Transfer.clear();INDArray image = loader.asMatrix(list.get(i)).div(255);Map<String, INDArray> map = vgg16Transfer.feedForward(image, false);labelMap.put(i, map.get("pool5"));}

    3、无目标攻击

    用第2步获取的张量作为label,用gradient ascent方法找到COSINE_PROXIMITY的极大值,COSINE_PROXIMITY的实现里将consine加了负号,所以是求cosine的最小值。换句话讲就是找到一张改动最小,且最不像自己的图片。代码如下

for (int i = 0; i < list.size(); i++) {vgg16Transfer.clear();INDArray oldImage = loader.asMatrix(list.get(i)).div(255);INDArray newImage = oldImage;NesterovsUpdater nesterovsUpdater = new NesterovsUpdater(0.9, 0.01, new long[] { 1, 3, 224, 224 });for (int m = 0; m < 2; m++) {vgg16Transfer.setInputs(newImage);vgg16Transfer.setLabels(labelMap.get(i).add(Nd4j.rand(new long[] { 1, 512, 7, 7 }, new NormalDistribution(0, 0.07))));vgg16Transfer.computeGradientAndScore();INDArray epsilon = Preprocessor.epsilon;epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(0, 30), NDArrayIndex.all())// 去掉30行.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(0, 75),NDArrayIndex.interval(0, 60))// 额头.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(0, 75),NDArrayIndex.interval(164, 224))// 额头.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(30, 45),NDArrayIndex.interval(0, 60))// 额头.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(30, 45),NDArrayIndex.interval(164, 224))// 额头.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(0, 75),NDArrayIndex.interval(72, 80))// 额头.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(0, 75),NDArrayIndex.interval(135, 152))// 额头.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(75, 115),NDArrayIndex.interval(0, 40))// 眼睛.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(75, 115),NDArrayIndex.interval(184, 224))// 眼睛.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(115, 165),NDArrayIndex.interval(0, 40))// 脸.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(115, 165),NDArrayIndex.interval(179, 224))// 脸.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(165, 195),NDArrayIndex.interval(0, 50))// 嘴巴.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(165, 195),NDArrayIndex.interval(174, 224))// 嘴巴.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(195, 224),NDArrayIndex.interval(0, 70))// 下巴.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(195, 224),NDArrayIndex.interval(154, 224))// 下巴.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(195, 224),NDArrayIndex.interval(75, 97))// 下巴.assign(0);epsilon.get(NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.interval(195, 224),NDArrayIndex.interval(127, 149))// 下巴.assign(0);epsilon = Transforms.sign(epsilon);nesterovsUpdater.applyUpdater(epsilon);INDArray preUpdate = newImage.add(epsilon);INDArray delta = oldImage.sub(preUpdate);INDArray tooLarge = delta.dup();// 因为减的太多了BooleanIndexing.replaceWhere(tooLarge, 0, Conditions.absLessThanOrEqual(max));BooleanIndexing.replaceWhere(tooLarge, 0, Conditions.lessThan(0));tooLarge.subi(max);BooleanIndexing.replaceWhere(tooLarge, 0, Conditions.lessThan(0));INDArray tooSmall = delta.dup();// 因为加的太多了BooleanIndexing.replaceWhere(tooSmall, 0, Conditions.absLessThanOrEqual(max));BooleanIndexing.replaceWhere(tooSmall, 0, Conditions.greaterThan(0));tooSmall.addi(max);BooleanIndexing.replaceWhere(tooSmall, 0, Conditions.greaterThan(0));INDArray bias = tooLarge.add(tooSmall);newImage = preUpdate.add(bias);vgg16Transfer.clear();System.out.println(vgg16Transfer.score());}System.out.println("平均偏差:" + (max - Transforms.abs(oldImage.sub(newImage)).meanNumber().doubleValue()));System.out.println("===========================");newImage = newImage.mul(255);BooleanIndexing.replaceWhere(newImage, 0, Conditions.lessThan(0.0));BooleanIndexing.replaceWhere(newImage, 255, Conditions.greaterThan(255));BufferedImage bufferedImage = new BufferedImage(224, 224, BufferedImage.TYPE_INT_RGB);imageLoader.toBufferedImageRGB(newImage.get(new INDArrayIndex[] { NDArrayIndex.point(0), NDArrayIndex.all(),NDArrayIndex.all(), NDArrayIndex.all() }), bufferedImage);ImageIO.write(bufferedImage, "jpg", new File("D:/preImage/" + list.get(i).getName()));INDArray oldSmallLoader = originalLoad.asMatrix(list.get(i)).div(255);INDArray smallImage = smallLoader.asMatrix(new File("D:/preImage/" + list.get(i).getName()));System.out.println("原始平均偏差:"+ (max - Transforms.abs(oldSmallLoader.sub(smallImage.div(255))).meanNumber().doubleValue()));System.out.println("===========================");BufferedImage smallBufferedImage = new BufferedImage(112, 112, BufferedImage.TYPE_INT_RGB);imageLoader.toBufferedImageRGB(smallImage.get(new INDArrayIndex[] { NDArrayIndex.point(0),NDArrayIndex.all(), NDArrayIndex.all(), NDArrayIndex.all() }), smallBufferedImage);ImageIO.write(smallBufferedImage, "jpg", new File("D:/images/" + list.get(i).getName()));}

    说明:

    (1)、目标图片为112*112的图片,经过尝试,先拓宽为224*224图片后再攻击,效果会提升。(推测dl4j vggface是用224*224的人脸进行训练,所以做了这个尝试)。

    (2)、在label中随机加入微小的噪声,可以提升效果,这段代码就是这个原因labelMap.get(i).add(Nd4j.rand(new long[] { 1, 512, 7, 7 }, new NormalDistribution(0, 0.07)))。

    (3)、在gradient ascent的过程中,同样加入动量作为更新因素效果会好一点,代码中的NesterovsUpdater就是实现这个功能。

    (4)、代码中BooleanIndexing.replaceWhere是为了将图像的改动量限定在一个范围上,实际上是一个clip操作,却写了这么多代码,这一点确实不如Python方便。

    (5)、为了进步提升分数,在更新图片时,最对嘴巴、眼睛、鼻子处进行变更,也能提升分数。

    (6)、目标Loss Function为COSINE_PROXIMITY,之前也尝试过MSE和MAE效果不佳。

    4、最终生成的攻击样本

如何用DL4J对人脸识别模型进行攻击

关于如何用DL4J对人脸识别模型进行攻击问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯