Windows10下用cuDNN运行DL4J时ND4J压缩器加载失败求助
问题
在Windows10系统下使用NVIDIA cuDNN库运行DL4J,配置了对应的pom依赖,并使用maven-assembly-plugin打包成Uber-Jar,运行时出现压缩器加载失败的错误,提示No compressors were found,完整报错信息及配置如下:
POM依赖配置
<properties> <java.version>16</java.version> <dl4j-master.version>1.0.0-M2.1</dl4j-master.version> <nd4j.backend>nd4j-cuda-11.6</nd4j.backend> <jcommon.version>1.0.24</jcommon.version> <jfreechart.version>1.0.13</jfreechart.version> <javacv.version>1.5.5</javacv.version> <logback.version>1.2.6</logback.version> <maven.compiler.source>16</maven.compiler.source> <maven.compiler.target>16</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.nd4j</groupId> <artifactId>${nd4j.backend}</artifactId> <version>${dl4j-master.version}</version> </dependency> <dependency> <groupId>org.nd4j</groupId> <artifactId>${nd4j.backend}</artifactId> <version>${dl4j-master.version}</version> <classifier>windows-x86_64-cudnn</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>cuda-platform-redist</artifactId> <version>11.6-8.3-1.5.7</version> </dependency> <dependency> <groupId>org.datavec</groupId> <artifactId>datavec-api</artifactId> <version>${dl4j-master.version}</version> </dependency> <dependency> <groupId>org.deeplearning4j</groupId> <artifactId>deeplearning4j-core</artifactId> <version>${dl4j-master.version}</version> </dependency> ...
打包配置
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.4.2</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <archive> <manifest> <mainClass>com.somename.Launcher</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build>
报错信息
java.lang.RuntimeException: Error loading ND4J Compressors via service loader: No compressors were found. This usually occurs when running ND4J UI from an uber-jar, which was built incorrectly (without services resource files being included)
完整日志:
10:39:06.143 [main] INFO org.nd4j.linalg.factory.Nd4jBackend - Loaded [JCublasBackend] backend 10:39:15.316 [main] INFO org.nd4j.nativeblas.NativeOpsHolder - Number of threads used for linear algebra: 32 10:39:15.363 [main] INFO org.nd4j.linalg.api.ops.executioner.DefaultOpExecutioner - Backend used: [CUDA]; OS: [Windows Server 2019] 10:39:15.363 [main] INFO org.nd4j.linalg.api.ops.executioner.DefaultOpExecutioner - Cores: [8]; Memory: [4,0GB]; 10:39:15.364 [main] INFO org.nd4j.linalg.api.ops.executioner.DefaultOpExecutioner - Blas vendor: [CUBLAS] 10:39:15.390 [main] INFO org.nd4j.linalg.jcublas.JCublasBackend - ND4J CUDA build version: 11.6.55 10:39:15.391 [main] INFO org.nd4j.linalg.jcublas.JCublasBackend - CUDA device 0: [Tesla T4]; cc: [7.5]; Total memory: [16105865216] 10:39:15.391 [main] INFO org.nd4j.linalg.jcublas.JCublasBackend - Backend build information: MSVC: 192930146 STD version: 201402L DEFAULT_ENGINE: samediff::ENGINE_CUDA HAVE_FLATBUFFERS HAVE_CUDNN 10:39:32.606 [main] ERROR org.nd4j.linalg.compression.BasicNDArrayCompressor - Error loading ND4J Compressors via service loader: No compressors were found. This usually occurs when running ND4J UI from an uber-jar, which was built incorrectly (without services resource files being included) Exception in thread "main" java.lang.ExceptionInInitializerError at org.nd4j.linalg.factory.Nd4j.getCompressor(Nd4j.java:5288) at org.nd4j.linalg.api.ndarray.BaseNDArray.getDouble(BaseNDArray.java:1785) at org.nd4j.linalg.api.ndarray.BaseNDArray.getInt(BaseNDArray.java:1753) at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.convertWritablesBatched(RecordReaderMultiDataSetIterator.java:413) at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.convertFeaturesOrLabels(RecordReaderMultiDataSetIterator.java:353) at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.nextMultiDataSet(RecordReaderMultiDataSetIterator.java:326) at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.next(RecordReaderMultiDataSetIterator.java:206) at org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator.next(RecordReaderDataSetIterator.java:346) at org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator.next(RecordReaderDataSetIterator.java:421) at org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator.next(RecordReaderDataSetIterator.java:53) at com.packagename.service.FAClassifierLearning.main(FAClassifierLearning.java:69) Caused by: java.lang.RuntimeException: Error loading ND4J Compressors via service loader: No compressors were found. This usually occurs when running ND4J UI from an uber-jar, which was built incorrectly (without services resource files being included) at org.nd4j.linalg.compression.BasicNDArrayCompressor.loadCompressors(BasicNDArrayCompressor.java:66) at org.nd4j.linalg.compression.BasicNDArrayCompressor.<init>(BasicNDArrayCompressor.java:46) at org.nd4j.linalg.compression.BasicNDArrayCompressor.<clinit>(BasicNDArrayCompressor.java:39) ... 11 more
解决方法
问题根源是Uber-Jar打包时,多个依赖中的META-INF/services文件被覆盖,ND4J压缩器通过Java ServiceLoader机制加载,这些服务文件丢失导致无法找到压缩器。以下是两种解决方案:
方案1:改用maven-shade-plugin打包
maven-shade-plugin默认支持合并META-INF/services文件,替换原有的assembly-plugin配置:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <!-- 合并所有META-INF/services下的文件 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <!-- 指定主类 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.somename.Launcher</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
方案2:修改assembly-plugin配置,添加服务文件合并规则
如果坚持使用assembly-plugin,需要自定义assembly描述文件来合并服务文件:
- 在项目根目录创建
src/main/assembly/uberjar.xml,内容如下:
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> <id>jar-with-dependencies</id> <formats> <format>jar</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <outputDirectory>/</outputDirectory> <useProjectArtifact>true</useProjectArtifact> <unpack>true</unpack> <scope>runtime</scope> </dependencySet> </dependencySets> <containerDescriptorHandlers> <!-- 合并META-INF/services目录下的所有文件 --> <containerDescriptorHandler implementation="org.apache.maven.plugins.assembly.handlers.service.ServiceHandler"/> </containerDescriptorHandlers> <archive> <manifest> <mainClass>com.somename.Launcher</mainClass> </manifest> </archive> </assembly>
- 修改pom中的assembly-plugin配置,引用自定义描述文件:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.4.2</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <descriptors> <descriptor>src/main/assembly/uberjar.xml</descriptor> </descriptors> </configuration> </plugin> </plugins> </build>
额外说明
你的pom依赖中nd4j-cuda-11.6已经包含了默认的ND4J压缩器实现,无需额外添加依赖,只要打包时保证META-INF/services文件不丢失,就能正常加载压缩器。
内容的提问来源于stack exchange,提问作者apollox

