1. 程式人生 > >deeplearning4j——卷積神經網絡對驗證碼進行識別

deeplearning4j——卷積神經網絡對驗證碼進行識別

www. 內部 采樣 視覺 inf rain agen ol3 mas

  一、前言
  
  計算機視覺長久以來沒有大的突破,卷積神經網絡的出現,給這一領域帶來了突破,本篇博客,將通過具體的實例來看看卷積神經網絡在圖像識別上的應用。
  
  導讀
  
  1、問題描述
  
  2、解決問題的思路
  
  3、用DL4J進行實現
  
  二、問題
  
  有如下一組驗證碼的圖片,圖片大小為60*160,驗證碼由5個數字組成,數字的範圍為0到9,並且每個驗證碼圖片上都加上了幹擾背景,圖片的文件名,表示驗證碼上的數字,樣本圖片如下:
  
  窮舉每張圖片的可能性幾乎不可能,所以傳統的程序思路不可能解這個問題,那麽必須讓計算機通過自我學習,獲取識別驗證碼的能力。先讓計算機看大量的驗證碼的圖片,並告訴計算機這些圖片的結果,讓計算機自我學習,慢慢地計算機就學會了識別驗證碼。
  
  三、解決思路
  
  1、特征
  
  每個數字的形狀各異,各自特征明顯,這裏的特征實際上指的是線條的走向、彎曲程度等等形狀上的不同表征,那麽對於偵測圖形上的形狀,卷積神經網絡加上Relu和Max采樣,可以很精確的做到這一點,本質原因在於,把卷積核拉直了看,本質上所做的事情就算向量的點積運算,求一個向量在另一個向量上的投影。對於卷積神經網絡的原理可以看看《有趣的卷積神經網絡》
  
  2、網絡結構設計
  
  對於每張圖片而言,有5個數字作為輸出結果,那麽得設計一個有5個output的深度神經網絡,首先用多個卷積核+Max采樣層的結構來抽取明顯特征,最後獲得的特征經過兩個全連接層逼近,這裏加全連接層有兩個目的,第一:經過sigmoid函數把值壓縮到0到1之間,便於softmax計算,第二,加上全連接層可以更加抽象特征,讓函數的逼近更加容易。最終的網絡結構如下:
  
  3、張量表示
  
  對於Label的表示用one-hot來表示,這樣可以很好的配合softmax,下圖展示了從0到9的數字表示,沿著行的方向,由上而下,分別表示0到9
  
  對於圖片上的像素點,值域在0到255之間,圖片如果是彩色,那麽實際上會有三個通道,這裏都是黑白色,所以,只有一個通道,取圖片上真實像素點的值,除以255進行歸一化即可。
  
  四、代碼實現
  
  1、網絡結構
  
  public static ComputationGraph createModel() {
  
  ComputationGraphConfiguration config = new NeuralNetConfiguration.Builder()
  
  .seed(seed)
  
  .gradientNormalization(GradientNormalization.RenormalizeL2PerLayer)
  
  .l2(1e-3)
  
  .updater(new Adam(1e-3))
  
  .weightInit( WeightInit.XAVIER_UNIFORM)
  
  .graphBuilder()
  
  .addInputs("trainFeatures")
  
  .setInputTypes(InputType.convolutional(60, 160, 1))
  
  .setOutputs("out1", "out2", "out3", "out4", "out5", "out6")
  
  .addLayer("cnn1", new ConvolutionLayer.Builder(new int[]{5, 5}, new int[]{1, 1}, new int[]{0, 0})
  
  .nIn(1).nOut(48).activation( Activation.RELU).build(), "trainFeatures")
  
  .addLayer("maxpool1", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,2}, new int[]{2, 2}, new int[]{0, 0})
  
  .build(), "cnn1")
  
  .addLayer("cnn2", new ConvolutionLayer.Builder(new int[]{5, 5}, new int[]{1, 1}, new int[]{0, 0})
  
  .nOut(64).activation( Activation.RELU).build(), "maxpool1")
  
  .addLayer("maxpool2", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,1}, new int[]{2, 1}, new int[]{0, 0})
  
  .build(), "cnn2")
  
  .addLayer("cnn3", new ConvolutionLayer.Builder(new int[]{3, 3}, new int[]{1, 1}, new int[]{0, 0})
  
  .nOut(128).activation( Activation.RELU).build(), "maxpool2")
  
  .addLayer("maxpool3", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,2}, new int[]{2, 2}, new int[]{0, 0})
  
  .build(), "cnn3")
  
  .addLayer("cnn4", new ConvolutionLayer.Builder(new int[]{4, 4}, new int[]{1, 1}, new int[]{0, 0})
  
  .nOut(256).activation( Activation.RELU).build(), "maxpool3")
  
  .addLayer("maxpool4", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,2}, new int[]{2, 2}, new int[]{0, 0})
  
  .build(), "cnn4")
  
  .addLayer("ffn0", new DenseLayer.Builder().nOut(3072)
  
  .build(), "maxpool4")
  
  .addLayer("ffn1", new DenseLayer.Builder().nOut(3072)
  
  .build(), "ffn0")
  
  .addLayer("out1", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
  
  .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1")
  
  .addLayer("out2", new www.gcyl158.com OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
  
  .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1")
  
  .addLayer("out3", www.gcyl152.com new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
  
  .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1")
  
  .addLayer("out4",www.mhylpt.com new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
  
  .nOut(10).activation(Activation.SOFTMAX).build(www.feifanyule.cn), "ffn1")
  
  .addLayer("out5", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
  
  .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1")
  
  .addLayer("out6", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
  
  .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1")
  
  .pretrain(false).backprop(true)
  
  .build();
  
  ComputationGraph model = new ComputationGraph(config);
  
  model.init();
  
  return model;
  
  }
  
  2、訓練集構建
  
  public MultiDataSet convertDataSet(int num) throws Exception {
  
  int batchNumCount = 0;
  
  INDArray[] featuresMask = null;
  
  INDArray[] labelMask = null;
  
  List<MultiDataSet> multiDataSets = new ArrayList<>();
  
  while (batchNumCount != num && fileIterator.hasNext()) {
  
  File image = fileIterator.next();
  
  String imageName = image.getName().substring(0,image.getName().lastIndexOf(‘.‘));
  
  String[] imageNames = imageName.split("");
  
  INDArray feature = asMatrix(image);
  
  INDArray[] features = new INDArray[]{feature};
  
  INDArray[] labels = new INDArray[6];
  
  Nd4j.getAffinityManager().ensureLocation(feature, AffinityManager.Location.DEVICE);
  
  if (imageName.length() < 6) {
  
  imageName = imageName + "0";
  
  imageNames = imageName.split("");
  
  }
  
  for (int i = 0; i < imageNames.length; i ++) {
  
  int digit = Integer.parseInt(imageNames[i]);
  
  labels[i] = Nd4j.zeros(1, 10).putScalar(new int[]{0, digit}, 1);
  
  }
  
  feature = feature.muli(1.0/255.0);
  
  multiDataSets.add(new MultiDataSet(features, labels, featuresMask, labelMask));
  
  batchNumCount ++;
  
  }
  
  MultiDataSet result = MultiDataSet.merge(multiDataSets);
  
  return result;
  
  }
  
  五、後記
  
  用deeplearning4j構建一個深度神經網絡,幾乎沒有多余的代碼,非常優雅就可以解一個復雜的圖像識別問題,對於上述代碼有幾點說明:
  
  1、對於DenseLayer層,這裏沒有設置網絡輸入的size,實際上在dl4j內部已經做了這個set操作
  
  2、對於梯度更新優化,這裏選用Adam,Adam融合了動量和自適應learningRate兩方面的因素,通常會有更好的效果
  
  3、損失函數用的類Log函數,和交叉熵有相同的效果
  
  4、模型訓練好可以使用 ModelSerializer.writeModel(model, modelPath, true)來保存網絡結構,就可以用於圖像識別了
  
  完整的代碼,可以查看deeplearning4j的example

deeplearning4j——卷積神經網絡對驗證碼進行識別