中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何用DL4J對人臉識別模型進行攻擊

發布時間:2021-12-21 13:45:49 來源:億速云 閱讀:154 作者:柒染 欄目:大數據

如何用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;

	@Override
	public INDArray preProcess(INDArray input, int miniBatchSize, LayerWorkspaceMgr workspaceMgr) {
		return workspaceMgr.dup(ArrayType.ACTIVATIONS, input);
	}

	@Override
	public InputType getOutputType(InputType inputType) {
		return inputType;
	}

	@Override
	public Pair<INDArray, MaskState> feedForwardMaskArray(INDArray maskArray, MaskState currentMaskState,
			int minibatchSize) {
		return null;
	}

	@Override
	public INDArray backprop(INDArray output, int miniBatchSize, LayerWorkspaceMgr workspaceMgr) {
		epsilon = output.detach();
		return workspaceMgr.dup(ArrayType.ACTIVATION_GRAD, output);
	}

	@Override
	public InputPreProcessor clone() {
		// TODO Auto-generated method stub
		return 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對人臉識別模型進行攻擊問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

专栏| 永昌县| 盐亭县| 黎城县| 左权县| 唐山市| 东光县| 饶阳县| 阿合奇县| 恩施市| 徐州市| 民勤县| 龙泉市| 海淀区| 巴东县| 阿克陶县| 东阿县| 城步| 昔阳县| 夏邑县| 万源市| 墨玉县| 乌拉特后旗| 沅江市| 台山市| 上高县| 贵港市| 芒康县| 班戈县| 江津市| 兴文县| 昭觉县| 龙泉市| 金华市| 沿河| 自治县| 开封市| 巴林左旗| 山阳县| 衡东县| 汾西县|