Spring Boot集成DeepLearning4j實現圖片數字識別

1.什么是DeepLearning4j?

DeepLearning4J(DL4J)是一套基于Java語言的神經網絡工具包,可以構建、定型和部署神經網絡。DL4J與Hadoop和Spark集成,支持分布式CPU和GPU,為商業環境(而非研究工具目的)所設計。Skymind是DL4J的商業支持機構。 Deeplearning4j擁有先進的技術,以即插即用為目標,通過更多預設的使用,避免多余的配置,讓非企業也能夠進行快速的原型制作。DL4J同時可以規模化定制。DL4J遵循Apache 2.0許可協議,一切以其為基礎的衍生作品均屬于衍生作品的作

Deeplearning4j的功能

Deeplearning4j包括了分布式、多線程的深度學習框架,以及普通的單線程深度學習框架。定型過程以集群進行,也就是說,Deeplearning4j可以快速處理大量數據。神經網絡可通過[迭代化簡]平行定型,與 Java、?Scala?和?Clojure?均兼容。Deeplearning4j在開放堆棧中作為模塊組件的功能,使之成為首個為微服務架構打造的深度學習框架。

???????

Deeplearning4j的組件

深度神經網絡能夠實現前所未有的準確度。對神經網絡的簡介請參見概覽頁。簡而言之,Deeplearning4j能夠讓你從各類淺層網絡(其中每一層在英文中被稱為layer)出發,設計深層神經網絡。這一靈活性使用戶可以根據所需,在分布式、生產級、能夠在分布式CPU或GPU的基礎上與Spark和Hadoop協同工作的框架內,整合受限玻爾茲曼機、其他自動編碼器、卷積網絡或遞歸網絡。 此處為我們已經建立的各個庫及其在系統整體中的所處位置: ?

dl4j-ecosystem-cn-small

DeepLearning4J用于設計神經網絡:

  • Deeplearning4j(簡稱DL4J)是為Java和Scala編寫的首個商業級開源分布式深度學習
  • DL4J與Hadoop和Spark集成,為商業環境(而非研究工具目的)所設計。
  • 支持GPU和CPU
  • 受到 Cloudera, Hortonwork, NVIDIA, Intel, IBM 等認證,可以在Spark, Flink, Hadoop 上運行
  • 支持并行迭代算法架構
  • DeepLearning4J的JavaDoc可在此處獲取
  • DeepLearning4J示例的Github代碼庫請見此處。相關示例的簡介匯總請見此處。
  • 開源工具 ASF 2.0許可證:github.com/deeplearning4j/deeplearning4j

2.訓練模型

訓練和測試數據集下載

https://raw.githubusercontent.com/zq2599/blog_download_files/master/files/mnist_png.tar.gz

MNIST簡介

  • MNIST是經典的計算機視覺數據集,來源是National Institute of Standards and Technology (NIST,美國國家標準與技術研究所),包含各種手寫數字圖片,其中訓練集60,000張,測試集 10,000張,
  • MNIST來源于250 個不同人的手寫,其中 50% 是高中學生, 50% 來自人口普查局 (the Census Bureau) 的工作人員.,測試集(test set) 也是同樣比例的手寫數字數據
  • MNIST官網:http://yann.lecun.com/exdb/mnist/

數據集簡介

從MNIST官網下載的原始數據并非圖片文件,需要按官方給出的格式說明做解析處理才能轉為一張張圖片,這些事情顯然不是本篇的主題,因此咱們可以直接使用DL4J為我們準備好的數據集(下載地址稍后給出),該數據集中是一張張獨立的圖片,這些圖片所在目錄的名字就是該圖片具體的數字

模型訓練

LeNet-5簡介

1351564-20180827204056354-1429986291

LeNet-5 結構:
  • 輸入層

圖片大小為 32×32×1,其中 1 表示為黑白圖像,只有一個 channel。

  • 卷積層

filter 大小 5×5,filter 深度(個數)為 6,padding 為 0, 卷積步長?s=1=1,輸出矩陣大小為 28×28×6,其中 6 表示 filter 的個數。

  • 池化層

average pooling,filter 大小 2×2(即?f=2=2),步長?s=2=2,no padding,輸出矩陣大小為 14×14×6。

  • 卷積層

filter 大小 5×5,filter 個數為 16,padding 為 0, 卷積步長?s=1=1,輸出矩陣大小為 10×10×16,其中 16 表示 filter 的個數。

  • 池化層

average pooling,filter 大小 2×2(即?f=2=2),步長?s=2=2,no padding,輸出矩陣大小為 5×5×16。注意,在該層結束,需要將 5×5×16 的矩陣flatten 成一個 400 維的向量。

  • 全連接層(Fully Connected layer,FC)

neuron 數量為 120。

  • 全連接層(Fully Connected layer,FC)

neuron 數量為 84。

  • 全連接層,輸出層

現在版本的 LeNet-5 輸出層一般會采用 softmax 激活函數,在 LeNet-5 提出的論文中使用的激活函數不是 softmax,但其現在不常用。該層神經元數量為 10,代表 0~9 十個數字類別。(圖 1 其實少畫了一個表示全連接層的方框,而直接用?^y^?表示輸出層。) ?

/******************************************************************************** Copyright (c) 2020 Konduit K.K.* Copyright (c) 2015-2019 Skymind, Inc.** This program and the accompanying materials are made available under the* terms of the Apache License, Version 2.0 which is available at* https://www.apache.org/licenses/LICENSE-2.0.** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the* License for the specific language governing permissions and limitations* under the License.** SPDX-License-Identifier: Apache-2.0******************************************************************************/package com.et.dl4j.model;import lombok.extern.slf4j.Slf4j;
import org.datavec.api.io.labels.ParentPathLabelGenerator;
import org.datavec.api.split.FileSplit;
import org.datavec.image.loader.NativeImageLoader;
import org.datavec.image.recordreader.ImageRecordReader;
import org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator;
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.inputs.InputType;
import org.deeplearning4j.nn.conf.layers.ConvolutionLayer;
import org.deeplearning4j.nn.conf.layers.DenseLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.conf.layers.SubsamplingLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.deeplearning4j.optimize.listeners.ScoreIterationListener;
import org.deeplearning4j.util.ModelSerializer;
import org.nd4j.evaluation.classification.Evaluation;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.dataset.api.preprocessor.DataNormalization;
import org.nd4j.linalg.dataset.api.preprocessor.ImagePreProcessingScaler;
import org.nd4j.linalg.learning.config.Nesterovs;
import org.nd4j.linalg.lossfunctions.LossFunctions;
import org.nd4j.linalg.schedule.MapSchedule;
import org.nd4j.linalg.schedule.ScheduleType;import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;/*** Implementation of LeNet-5 for handwritten digits image classification on MNIST dataset (99% accuracy)* <a href="http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf">[LeCun et al., 1998. Gradient based learning applied to document recognition]</a>* Some minor changes are made to the architecture like using ReLU and identity activation instead of* sigmoid/tanh, max pooling instead of avg pooling and softmax output layer.* <p>* This example will download 15 Mb of data on the first run.** @author hanlon* @author agibsonccc* @author fvaleri* @author dariuszzbyrad*/
@Slf4j
public class LeNetMNISTReLu {//dataset github:https://raw.githubusercontent.com/zq2599/blog_download_files/master/files/mnist_png.tar.gz// 存放文件的地址,請酌情修改
//    private static final String BASE_PATH = System.getProperty("java.io.tmpdir") + "/mnist";private static final String BASE_PATH = "/Users/liuhaihua/Downloads";public static void main(String[] args) throws Exception {// 圖片像素高int height = 28;// 圖片像素寬int width = 28;// 因為是黑白圖像,所以顏色通道只有一個int channels = 1;// 分類結果,0-9,共十種數字int outputNum = 10;// 批大小int batchSize = 54;// 循環次數int nEpochs = 1;// 初始化偽隨機數的種子int seed = 1234;// 隨機數工具Random randNumGen = new Random(seed);log.info("檢查數據集文件夾是否存在:{}", BASE_PATH + "/mnist_png");if (!new File(BASE_PATH + "/mnist_png").exists()) {log.info("數據集文件不存在,請下載壓縮包并解壓到:{}", BASE_PATH);return;}// 標簽生成器,將指定文件的父目錄作為標簽ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator();// 歸一化配置(像素值從0-255變為0-1)DataNormalization imageScaler = new ImagePreProcessingScaler();// 不論訓練集還是測試集,初始化操作都是相同套路:// 1. 讀取圖片,數據格式為NCHW// 2. 根據批大小創建的迭代器// 3. 將歸一化器作為預處理器log.info("訓練集的矢量化操作...");// 初始化訓練集File trainData = new File(BASE_PATH + "/mnist_png/training");FileSplit trainSplit = new FileSplit(trainData, NativeImageLoader.ALLOWED_FORMATS, randNumGen);ImageRecordReader trainRR = new ImageRecordReader(height, width, channels, labelMaker);trainRR.initialize(trainSplit);DataSetIterator trainIter = new RecordReaderDataSetIterator(trainRR, batchSize, 1, outputNum);// 擬合數據(實現類中實際上什么也沒做)imageScaler.fit(trainIter);trainIter.setPreProcessor(imageScaler);log.info("測試集的矢量化操作...");// 初始化測試集,與前面的訓練集操作類似File testData = new File(BASE_PATH + "/mnist_png/testing");FileSplit testSplit = new FileSplit(testData, NativeImageLoader.ALLOWED_FORMATS, randNumGen);ImageRecordReader testRR = new ImageRecordReader(height, width, channels, labelMaker);testRR.initialize(testSplit);DataSetIterator testIter = new RecordReaderDataSetIterator(testRR, batchSize, 1, outputNum);testIter.setPreProcessor(imageScaler); // same normalization for better resultslog.info("配置神經網絡");// 在訓練中,將學習率配置為隨著迭代階梯性下降Map<Integer, Double> learningRateSchedule = new HashMap<>();learningRateSchedule.put(0, 0.06);learningRateSchedule.put(200, 0.05);learningRateSchedule.put(600, 0.028);learningRateSchedule.put(800, 0.0060);learningRateSchedule.put(1000, 0.001);// 超參數MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(seed)// L2正則化系數.l2(0.0005)// 梯度下降的學習率設置.updater(new Nesterovs(new MapSchedule(ScheduleType.ITERATION, learningRateSchedule)))// 權重初始化.weightInit(WeightInit.XAVIER)// 準備分層.list()// 卷積層.layer(new ConvolutionLayer.Builder(5, 5).nIn(channels).stride(1, 1).nOut(20).activation(Activation.IDENTITY).build())// 下采樣,即池化.layer(new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX).kernelSize(2, 2).stride(2, 2).build())// 卷積層.layer(new ConvolutionLayer.Builder(5, 5).stride(1, 1) // nIn need not specified in later layers.nOut(50).activation(Activation.IDENTITY).build())// 下采樣,即池化.layer(new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX).kernelSize(2, 2).stride(2, 2).build())// 稠密層,即全連接.layer(new DenseLayer.Builder().activation(Activation.RELU).nOut(500).build())// 輸出.layer(new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD).nOut(outputNum).activation(Activation.SOFTMAX).build()).setInputType(InputType.convolutionalFlat(height, width, channels)) // InputType.convolutional for normal image.build();MultiLayerNetwork net = new MultiLayerNetwork(conf);net.init();// 每十個迭代打印一次損失函數值net.setListeners(new ScoreIterationListener(10));log.info("神經網絡共[{}]個參數", net.numParams());long startTime = System.currentTimeMillis();// 循環操作for (int i = 0; i < nEpochs; i++) {log.info("第[{}]個循環", i);net.fit(trainIter);Evaluation eval = net.evaluate(testIter);log.info(eval.stats());trainIter.reset();testIter.reset();}log.info("完成訓練和測試,耗時[{}]毫秒", System.currentTimeMillis()-startTime);// 保存模型File ministModelPath = new File(BASE_PATH + "/minist-model.zip");ModelSerializer.writeModel(net, ministModelPath, true);log.info("最新的MINIST模型保存在[{}]", ministModelPath.getPath());}
}

輸出模型文件和得分結果

dl2

3.編寫模型預測接口

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springboot-demo</artifactId><groupId>com.et</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>Deeplearning4j</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><dl4j-master.version>1.0.0-beta7</dl4j-master.version><nd4j.backend>nd4j-native</nd4j.backend></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency><dependency><groupId>org.deeplearning4j</groupId><artifactId>deeplearning4j-core</artifactId><version>${dl4j-master.version}</version></dependency><dependency><groupId>org.nd4j</groupId><artifactId>${nd4j.backend}</artifactId><version>${dl4j-master.version}</version></dependency><!--用于本地GPU--><!--        <dependency>--><!--            <groupId>org.deeplearning4j</groupId>--><!--            <artifactId>deeplearning4j-cuda-9.2</artifactId>--><!--            <version>${dl4j-master.version}</version>--><!--        </dependency>--><!--        <dependency>--><!--            <groupId>org.nd4j</groupId>--><!--            <artifactId>nd4j-cuda-9.2-platform</artifactId>--><!--            <version>${dl4j-master.version}</version>--><!--        </dependency>--></dependencies>
</project>

cotroller

package com.et.dl4j.controller;import com.et.dl4j.service.PredictService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.util.HashMap;
import java.util.Map;@RestController
public class HelloWorldController {@RequestMapping("/hello")public Map<String, Object> showHelloWorld(){Map<String, Object> map = new HashMap<>();map.put("msg", "HelloWorld");return map;}@AutowiredPredictService predictService;@PostMapping("/predict-with-black-background")public int predictWithBlackBackground(@RequestParam("file") MultipartFile file) throws Exception {// 訓練模型的時候,用的數字是白字黑底,// 因此如果上傳白字黑底的圖片,可以直接拿去識別,而無需反色處理return predictService.predict(file, false);}@PostMapping("/predict-with-white-background")public int predictWithWhiteBackground(@RequestParam("file") MultipartFile file) throws Exception {// 訓練模型的時候,用的數字是白字黑底,// 因此如果上傳黑字白底的圖片,就需要做反色處理,// 反色之后就是白字黑底了,可以拿去識別return predictService.predict(file, true);}
}

service

package com.et.dl4j.service;import org.springframework.web.multipart.MultipartFile;public interface PredictService {/*** 取得上傳的圖片,做轉換后識別成數字* @param file 上傳的文件* @param isNeedRevert 是否要做反色處理* @return*/int predict(MultipartFile file, boolean isNeedRevert) throws Exception ;
}
package com.et.dl4j.service.impl;
import com.et.dl4j.service.PredictService;
import com.et.dl4j.util.ImageFileUtil;
import lombok.extern.slf4j.Slf4j;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.util.ModelSerializer;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.PostConstruct;
import java.io.File;@Service
@Slf4j
public class PredictServiceImpl implements PredictService {/*** -1表示識別失敗*/private static final int RLT_INVALID = -1;/*** 模型文件的位置*/@Value("${predict.modelpath}")private String modelPath;/*** 處理圖片文件的目錄*/@Value("${predict.imagefilepath}")private String imageFilePath;/*** 神經網絡*/private MultiLayerNetwork net;/*** bean實例化成功就加載模型*/@PostConstructprivate void loadModel() {log.info("load model from [{}]", modelPath);// 加載模型try {net = ModelSerializer.restoreMultiLayerNetwork(new File(modelPath));log.info("module summary\n{}", net.summary());} catch (Exception exception) {log.error("loadModel error", exception);}}@Overridepublic int predict(MultipartFile file, boolean isNeedRevert) throws Exception {log.info("start predict, file [{}], isNeedRevert [{}]", file.getOriginalFilename(), isNeedRevert);// 先存文件String rawFileName = ImageFileUtil.save(imageFilePath, file);if (null==rawFileName) {return RLT_INVALID;}// 反色處理后的文件名String revertFileName = null;// 調整大小后的文件名String resizeFileName;// 是否需要反色處理if (isNeedRevert) {// 把原始文件做反色處理,返回結果是反色處理后的新文件revertFileName = ImageFileUtil.colorRevert(imageFilePath, rawFileName);// 把反色處理后調整為28*28大小的文件resizeFileName = ImageFileUtil.resize(imageFilePath, revertFileName);} else {// 直接把原始文件調整為28*28大小的文件resizeFileName = ImageFileUtil.resize(imageFilePath, rawFileName);}// 現在已經得到了結果反色和調整大小處理過后的文件,// 那么原始文件和反色處理過的文件就可以刪除了ImageFileUtil.clear(imageFilePath, rawFileName, revertFileName);// 取出該黑白圖片的特征INDArray features = ImageFileUtil.getGrayImageFeatures(imageFilePath, resizeFileName);// 將特征傳給模型去識別return net.predict(features)[0];}
}

application.properties

# 上傳文件總的最大值
spring.servlet.multipart.max-request-size=1024MB# 單個文件的最大值
spring.servlet.multipart.max-file-size=10MB# 處理圖片文件的目錄
predict.imagefilepath=/Users/liuhaihua/Downloads/images/# 模型所在位置
predict.modelpath=/Users/liuhaihua/Downloads/minist-model.zip

工具類

package com.et.dl4j.util;import lombok.extern.slf4j.Slf4j;
import org.datavec.api.split.FileSplit;
import org.datavec.image.loader.NativeImageLoader;
import org.datavec.image.recordreader.ImageRecordReader;
import org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.dataset.api.preprocessor.ImagePreProcessingScaler;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.UUID;@Slf4j
public class ImageFileUtil {/*** 調整后的文件寬度*/public static final int RESIZE_WIDTH = 28;/*** 調整后的文件高度*/public static final int RESIZE_HEIGHT = 28;/*** 將上傳的文件存在服務器上* @param base 要處理的文件所在的目錄* @param file 要處理的文件* @return*/public static String save(String base, MultipartFile file) {// 檢查是否為空if (file.isEmpty()) {log.error("invalid file");return null;}// 文件名來自原始文件String fileName = file.getOriginalFilename();// 要保存的位置File dest = new File(base + fileName);// 開始保存try {file.transferTo(dest);} catch (IOException e) {log.error("upload fail", e);return null;}return fileName;}/*** 將圖片轉為28*28像素* @param base     處理文件的目錄* @param fileName 待調整的文件名* @return*/public static String resize(String base, String fileName) {// 新文件名是原文件名在加個隨機數后綴,而且擴展名固定為pngString resizeFileName = fileName.substring(0, fileName.lastIndexOf(".")) + "-" + UUID.randomUUID() + ".png";log.info("start resize, from [{}] to [{}]", fileName, resizeFileName);try {// 讀原始文件BufferedImage bufferedImage = ImageIO.read(new File(base + fileName));// 縮放后的實例Image image = bufferedImage.getScaledInstance(RESIZE_WIDTH, RESIZE_HEIGHT, Image.SCALE_SMOOTH);BufferedImage resizeBufferedImage = new BufferedImage(28, 28, BufferedImage.TYPE_INT_RGB);Graphics graphics = resizeBufferedImage.getGraphics();// 繪圖graphics.drawImage(image, 0, 0, null);graphics.dispose();// 轉換后的圖片寫文件ImageIO.write(resizeBufferedImage, "png", new File(base + resizeFileName));} catch (Exception exception) {log.info("resize error from [{}] to [{}], {}", fileName, resizeFileName, exception);resizeFileName = null;}log.info("finish resize, from [{}] to [{}]", fileName, resizeFileName);return resizeFileName;}/*** 將RGB轉為int數字* @param alpha* @param red* @param green* @param blue* @return*/private static int colorToRGB(int alpha, int red, int green, int blue) {int pixel = 0;pixel += alpha;pixel = pixel << 8;pixel += red;pixel = pixel << 8;pixel += green;pixel = pixel << 8;pixel += blue;return pixel;}/*** 反色處理* @param base 處理文件的目錄* @param src 用于處理的源文件* @return 反色處理后的新文件* @throws IOException*/public static String colorRevert(String base, String src) throws IOException {int color, r, g, b, pixel;// 讀原始文件BufferedImage srcImage = ImageIO.read(new File(base + src));// 修改后的文件BufferedImage destImage = new BufferedImage(srcImage.getWidth(), srcImage.getHeight(), srcImage.getType());for (int i=0; i<srcImage.getWidth(); i++) {for (int j=0; j<srcImage.getHeight(); j++) {color = srcImage.getRGB(i, j);r = (color >> 16) & 0xff;g = (color >> 8) & 0xff;b = color & 0xff;pixel = colorToRGB(255, 0xff - r, 0xff - g, 0xff - b);destImage.setRGB(i, j, pixel);}}// 反射文件的名字String revertFileName =  src.substring(0, src.lastIndexOf(".")) + "-revert.png";// 轉換后的圖片寫文件ImageIO.write(destImage, "png", new File(base + revertFileName));return revertFileName;}/*** 取黑白圖片的特征* @param base* @param fileName* @return* @throws Exception*/public static INDArray getGrayImageFeatures(String base, String fileName) throws Exception {log.info("start getImageFeatures [{}]", base + fileName);// 和訓練模型時一樣的設置ImageRecordReader imageRecordReader = new ImageRecordReader(RESIZE_HEIGHT, RESIZE_WIDTH, 1);FileSplit fileSplit = new FileSplit(new File(base + fileName),NativeImageLoader.ALLOWED_FORMATS);imageRecordReader.initialize(fileSplit);DataSetIterator dataSetIterator = new RecordReaderDataSetIterator(imageRecordReader, 1);dataSetIterator.setPreProcessor(new ImagePreProcessingScaler(0, 1));// 取特征return dataSetIterator.next().getFeatures();}/*** 批量清理文件* @param base      處理文件的目錄* @param fileNames 待清理文件集合*/public static void clear(String base, String...fileNames) {for (String fileName : fileNames) {if (null==fileName) {continue;}File file = new File(base + fileName);if (file.exists()) {file.delete();}}}}

DemoApplication.java

package com.et.dl4j;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

以上只是一些關鍵代碼,所有代碼請參見下面代碼倉庫

代碼倉庫

  • https://github.com/Harries/springboot-demo

4.測試

啟動Spring Boot應用,上傳圖片測試

  • 如果用戶輸入的是黑底白字的圖片,只需要將上述流程中的反色處理去掉即可
  • 為白底黑字圖片提供專用接口predict-with-white-background
  • 為黑底白字圖片提供專用接口predict-with-black-background

dl3

5.引用

  • 關于我們 - Deeplearning4j: Open-source, Distributed Deep Learning for the JVM
  • DL4J實戰之三:經典卷積實例(LeNet-5)_multilayerconfiguration 參數-CSDN博客
  • Spring Boot集成DeepLearning4j實現圖片數字識別 | Harries Blog?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/38003.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/38003.shtml
英文地址,請注明出處:http://en.pswp.cn/web/38003.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《昇思25天學習打卡營第28天 | 昇思MindSporeVision Transformer圖像分類》

第28天 本節學習了Vision Transformer圖像分類 ViT則是自然語言處理和計算機視覺兩個領域的融合結晶。在不依賴卷積操作的情況下&#xff0c;依然可以在圖像分類任務上達到很好的效果。ViT模型的主體結構是基于Transformer模型的Encoder部分&#xff08;部分結構順序有調整&a…

Mojo: AI開發的革命性編程語言

------------->更多內容&#xff0c;請移步“魯班秘笈”&#xff01;&#xff01;<------------- 剛剛Mojo和HuggingFace一樣發布新版本&#xff0c;此版本編號為24.4&#xff0c;包括幾個核心語言和標準庫增強功能。此版本最大的亮點之一是收到了來自18個社區貢獻者的21…

redis布隆過濾器原理及應用場景

目錄 原理 應用場景 優點 缺點 布隆過濾器&#xff08;Bloom Filter&#xff09;是一種空間效率很高的隨機數據結構&#xff0c;它利用位數組和哈希函數來判斷一個元素是否存在于集合中。 原理 數據結構&#xff1a; 位數組&#xff1a;一個由0和1組成的數組&#xff0c;初始…

【AI應用探討】—膠囊神經網絡(GNN)應用場景

目錄 一、圖像識別與分類 二、目標檢測與跟蹤 三、自然語言處理 四、其他應用場景 一、圖像識別與分類 多層次特征捕捉 膠囊網絡通過其獨特的膠囊結構和動態路由算法&#xff0c;能夠捕捉圖像中的多層次特征。每個膠囊不僅輸出激活強度&#xff0c;還輸出一個多維向量&…

Docker Desktop安裝mysql

安裝 Docker Desktop 1、訪問 Docker Desktop 官方下載地址&#xff1a;https://www.docker.com/products/docker-desktop/ &#xff0c; 選擇對應平臺的 Docker Desktop 安裝包點擊下載&#xff1a; 2、下載成功后&#xff0c;雙擊開始安裝&#xff1a; 3、安裝之前的相關配…

產品經理-對產品經理的認識(1)

今天跟大家聊一下產品經理這個崗位的,產品經理是互聯網崗位當中比較火的一個崗位,也是最接近CEO的崗位 產品經理崗位&#xff0c;技術門檻低&#xff0c;薪水和前景都很不錯&#xff0c;又處于團隊的核心位置 產品經理崗位沒有完全相關的專業設置和清晰的學習路徑&#xff0c;絕…

Spring Boot中實現多數據源配置

Spring Boot中實現多數據源配置 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討如何在Spring Boot應用中實現多數據源配置&#xff0c;以應對復雜…

java框架的落地實踐案例:大數據平臺設計與實現

使用 java 框架設計和實現大數據平臺可為企業提供數據處理和分析解決方案&#xff0c;使之能夠做出數據驅動的決策。系統采用微服務架構&#xff0c;分解數據處理任務為松散耦合組件&#xff0c;構建于 spring boot 等 java 框架之上。數據采集通過kafka 進行&#xff0c;數據清…

Rust借助dotenv庫讀取環境變量

這里寫自定義目錄標題 歡迎使用Markdown編輯器新的改變功能快捷鍵合理的創建標題&#xff0c;有助于目錄的生成如何改變文本的樣式插入鏈接與圖片如何插入一段漂亮的代碼片生成一個適合你的列表創建一個表格設定內容居中、居左、居右SmartyPants 創建一個自定義列表如何創建一個…

(親測有效)2024代替電視家的app,電視家停了還有什么軟件可以看電視?

嘿&#xff0c;大家好&#xff0c;我是阿星&#xff0c;今天又來跟大家聊聊那些讓人眼前一亮的電視直播軟件。咱們這回不聊那些老掉牙的&#xff0c;來點新鮮的&#xff0c;讓咱們的電視屏幕也能跟上潮流&#xff0c;享受一下科技帶來的便利和樂趣。 首先&#xff0c;得提一提…

如何評價CSS框架TailwindCSS?

端午三天&#xff0c;你們在放假&#xff0c;而我&#xff0c;一個人躲在家里&#xff0c;苦練 tailwindcss。 我在準備這樣一個學習項目&#xff0c;它與傳統的文章/視頻類學習不同&#xff0c;我會在教程中內置大量的可交互案例&#xff0c;提供沉浸式的學習體驗&#xff0c…

SpringMVC中執行流程

文章目錄 14、SpringMVC執行流程14.1、SpringMVC常用組件14.2、DispatcherServlet初始化過程①初始化WebApplicationContext②創建WebApplicationContext③DispatcherServlet初始化策略 14.3、DispatcherServlet調用組件處理請求①processRequest()②doService()③doDispatch()…

Zynq7000系列FPGA中DMA引擎編程指南

DMA引擎的編程指南通常涉及一系列步驟和API調用&#xff0c;以確保數據在內存之間的高效傳輸&#xff0c;而無需CPU的直接干預。 DMA引擎的編程指南包括以下部分&#xff1a; 一、編寫微代碼為AXI事務編寫CCRx程序 通道微碼用于設置dmac.CCRx寄存器以定義AXI事務的屬性。這是…

TikTok直播限流與網絡的關系及解決方法

TikTok作為一款熱門的社交平臺&#xff0c;其直播功能吸引了大量用戶。然而&#xff0c;一些用戶可能會遇到TikTok直播限流的問題&#xff0c;例如直播過程中出現播放量低、直播畫面質量差等情況。那么&#xff0c;TikTok直播限流與所使用的網絡線路是否有關系&#xff1f;是否…

學習springIOC

第二章 Spring IOC 章節內容 Spring IOC技術實現Spring IOC設值注入Spring IOC構造注入 章節目標 掌握Spring IOC技術實現掌握Spring IOC設置注入掌握Spring IOC構造注入 第一節 Spring簡介 1. Spring 簡介 Spring 是目前主流的 Java 開發框架&#xff0c;是 Java 世界最…

Android實現手寫輸入

android應用開發中有時會有手寫輸入需求&#xff0c;非通過系統鍵盤手寫功能&#xff0c;比如自定義鍵盤&#xff0c;這時就需要自己來實現&#xff0c;一般有兩種場景&#xff1a;一種是類似手寫簽名保存&#xff1b;另一種是真正的手寫輸入&#xff0c;需要將筆跡識別成正確的…

JTracker IDEA 中最好的 MyBatis 日志格式化插件

前言 如果你使用 MyBatis ORM 框架&#xff0c;那么你應該用過 MyBatis Log 格式化插件&#xff0c;它可以讓我們的程序輸出的日志更人性化。 但是有一個問題&#xff0c;通常我們只能看到格式化后的效果&#xff0c;沒辦法知道這個 SQL 是誰執行的以及調用的鏈路。 如下圖所…

文章解讀與仿真程序復現思路——電網技術EI\CSCD\北大核心《考慮復合指標優化模態分解和 Stacking 集成的綜合能源系統多元負荷預測》

本專欄欄目提供文章與程序復現思路&#xff0c;具體已有的論文與論文源程序可翻閱本博主免費的專欄欄目《論文與完整程序》 論文與完整源程序_電網論文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 電網論文源程序-CSDN博客電網論文源…

【stm32】大一上學期筆記復制

砌墻單片機 外設是什么&#xff1f; ipage 8 nx軸 128 X0-127 y0-63 PWM脈沖寬度調制 PWM脈沖寬度調制 2023年10月13日 基本特性&#xff1a;脈沖寬度調制PWM是一種對模擬信號進行數字編碼的方法。廣泛引用于電機控制&#xff0c;燈光的亮度調節&#xff0c;功率控制等領域…

趕緊收藏!全網最佳WebSocket封裝:完美支持斷網重連,自動心跳!

文章目錄 一、WebSocket封裝庫簡介二、庫的安裝與配置2.1 安裝2.2 初始化 三、功能詳解3.1 斷網重連3.2 自動心跳3.3 消息隊列3.4 事件管理 四、示例代碼五、總結 &#x1f389;歡迎來到Java學習路線專欄~探索Java中的靜態變量與實例變量 ☆* o(≧▽≦)o *☆嗨~我是IT陳寒&…