1、前言 java
計算機視覺長久以來沒有大的突破,卷積神經網絡的出現,給這一領域帶來了突破,本篇博客,將經過具體的實例來看看卷積神經網絡在圖像識別上的應用。git
導讀網絡
一、問題描述函數
二、解決問題的思路學習
三、用DL4J進行實現優化
2、問題ui
有以下一組驗證碼的圖片,圖片大小爲60*160,驗證碼由5個數字組成,數字的範圍爲0到9,而且每一個驗證碼圖片上都加上了干擾背景,圖片的文件名,表示驗證碼上的數字,樣本圖片以下:.net
窮舉每張圖片的可能性幾乎不可能,因此傳統的程序思路不可能解這個問題,那麼必須讓計算機經過自我學習,獲取識別驗證碼的能力。先讓計算機看大量的驗證碼的圖片,並告訴計算機這些圖片的結果,讓計算機自我學習,慢慢地計算機就學會了識別驗證碼。設計
3、解決思路3d
一、特徵
每一個數字的形狀各異,各自特徵明顯,這裏的特徵實際上指的是線條的走向、彎曲程度等等形狀上的不一樣表徵,那麼對於偵測圖形上的形狀,卷積神經網絡加上Relu和Max採樣,能夠很精確的作到這一點,本質緣由在於,把卷積核拉直了看,本質上所作的事情就算向量的點積運算,求一個向量在另外一個向量上的投影。對於卷積神經網絡的原理能夠看看《有趣的卷積神經網絡》
二、網絡結構設計
對於每張圖片而言,有5個數字做爲輸出結果,那麼得設計一個有5個output的深度神經網絡,首先用多個卷積核+Max採樣層的結構來抽取明顯特徵,最後得到的特徵通過兩個全鏈接層逼近,這裏加全鏈接層有兩個目的,第一:通過sigmoid函數把值壓縮到0到1之間,便於softmax計算,第二,加上全鏈接層能夠更加抽象特徵,讓函數的逼近更加容易。最終的網絡結構以下:
三、張量表示
對於Label的表示用one-hot來表示,這樣能夠很好的配合softmax,下圖展現了從0到9的數字表示,沿着行的方向,由上而下,分別表示0到9
對於圖片上的像素點,值域在0到255之間,圖片若是是彩色,那麼實際上會有三個通道,這裏都是黑白色,因此,只有一個通道,取圖片上真實像素點的值,除以255進行歸一化便可。
4、代碼實現
一、網絡結構
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 OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out3", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out4", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "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; }
二、訓練集構建
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; }
5、後記
用deeplearning4j構建一個深度神經網絡,幾乎沒有多餘的代碼,很是優雅就能夠解一個複雜的圖像識別問題,對於上述代碼有幾點說明:
一、對於DenseLayer層,這裏沒有設置網絡輸入的size,實際上在dl4j內部已經作了這個set操做
二、對於梯度更新優化,這裏選用Adam,Adam融合了動量和自適應learningRate兩方面的因素,一般會有更好的效果
三、損失函數用的類Log函數,和交叉熵有相同的效果
四、模型訓練好可使用 ModelSerializer.writeModel(model, modelPath, true)來保存網絡結構,就能夠用於圖像識別了
完整的代碼,能夠查看deeplearning4j的example
---------------------------------------------------------------------------------------------------------
快樂源於分享。
此博客乃做者原創, 轉載請註明出處