最近在用DeepLearning4J(DL4J)尝试语音识别的深度学习,git DL4J的代码,用IntelliJ IDEA打开,配置好相关依赖后,运行包org.deeplearning4j.examples.test.Test的main,可以正常运行,但是有警告提示如下:

十一月 27, 2015 12:37:07 下午 com.github.fommil.netlib.BLAS <clinit>
WARNING: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS
十一月 27, 2015 12:37:09 下午 com.github.fommil.jni.JniLoader liberalLoad
****************************************************************
INFO: successfully loaded C:\Users\ADMINI~1\AppData\Local\Temp\jniloader6882206374132167742netlib-native_ref-win-x86_64.dll
WARNING: COULD NOT LOAD NATIVE SYSTEM BLAS
ND4J performance WILL be reduced
Please install native BLAS library such as OpenBLAS or IntelMKL
See http://nd4j.org/getstarted.html#open for further details
****************************************************************

提示无法加载com.github.fommil.netlib.NativeSystemBLAS,和无法加载native system blas,DN4J的性能会受到影响.

查了github,stackoverflow,quora等,找到了如下网页.github的是一个遇到类似问题的人抱怨native blas难以配置,害的自己在源码中才找到解决方法,而nd4j的程序员回答所有的深度学习框架中的native blas都是难配置的,我在搜索的时候也发现了MLib等库确实也会报这个警告.第二个博客是一篇讲述如何配置blas的文章.

https://github.com/deeplearning4j/nd4j/issues/456
http://avulanov.blogspot.cz/2014/09/howto-to-run-netlib-javabreeze-in.html

这篇文章中,我想说明一下几个问题:

1. native blas 是本地库,用C/C++写成,因而运算速度较快.

2. Java如果要调用C/C++的dll,一定要用JNI技术来调用对应的dll,那么dll的路径和名称分别是什么?

3. 如果找到需要的dll,假设叫a.dll,如果a.dll又依赖于b.dll和c.dll,那么把a.dll,b.dll,c.dll都放在Java识别的路径下,是不是就可以解决这个问题了.

1.dll的存放路径和名称

先看DL4J的源码,错误首先出现在如下代码中

Nd4j.getRandom().setSeed(seed);

进入Nd4j的类,加断点,单步调试,经过如下的函数调用栈后,进入NativeSystemBLAS这个类.


我们看下NativeSystemBLAS类的内容,在static静态块中找到如下用于加载dll的代码:

static {String jnilib = JniNamer.getJniName("netlib-native_system");String natives = System.getProperty("com.github.fommil.netlib.NativeSystemBLAS.natives", jnilib);JniLoader.load(natives.split(","));}

先加断点单步运行,看看jnilib的内容,运行后得到的是"netlib-native_system-win-x86_64.dll",为什么是这个,我的电脑是64位,如果32位系统又会需要哪个dll库呢?

我们进入getJniName函数,看下其代码,这里arch用于获得架构,os用于获得系统版本,extension获得后缀,然后拼出dll的名称:

    public static String getJniName(String stem) {String arch = arch();//获得系统架构,x86,i386对应于i686,x86_64,amd64对应于x86_64,其他还有arm等架构String abi = abi(arch);//判断是不是arm架构,如果是arm架构,需要其他的设置,这里不问String os = os();//判断系统是win,linux.还是mac os xString extension = extension(os);//根据系统版本,确定文件后缀名是dll,还是so,osxreturn stem + "-" + os + "-" + arch + abi + "." + extension;//返回确定的库名字}

arch代码如下:

private static String arch() {String arch = System.getProperty("os.arch", "").toLowerCase();if(!arch.equals("x86") && !arch.equals("i386") && !arch.equals("i486") && !arch.equals("i586") && !arch.equals("i686")) {if(!arch.equals("x86_64") && !arch.equals("amd64")) {if(arch.equals("ia64")) {return "ia64";} else if(arch.equals("arm")) {return "arm";} else if(arch.equals("armv5l")) {return "armv5l";} else if(arch.equals("armv6l")) {return "armv6l";} else if(arch.equals("armv7l")) {return "armv7l";} else if(arch.equals("sparc")) {return "sparc";} else if(arch.equals("sparcv9")) {return "sparcv9";} else if(arch.equals("pa_risc2.0")) {return "risc2";} else if(arch.equals("ppc")) {return "ppc";} else if(arch.startsWith("ppc")) {return "ppc64";} else {log.warning("unrecognised architecture: " + arch);return "unknown";}} else {return "x86_64";}} else {return "i686";}}

abi代码如下:

private static String abi(String arch) {if(!arch.startsWith("arm")) {return "";} else {try {String[] e = new String[]{"sun.boot.library.path", "java.library.path", "java.home"};int len$ = e.length;int i$;String dir;for(i$ = 0; i$ < len$; ++i$) {dir = e[i$];String file = System.getProperty(dir, "");log.config(dir + ": " + file);if(file.matches(".*(gnueabihf|armhf).*")) {return "hf";}}e = new String[]{"/lib/arm-linux-gnueabihf", "/usr/lib/arm-linux-gnueabihf"};len$ = e.length;for(i$ = 0; i$ < len$; ++i$) {dir = e[i$];File var7 = new File(dir);if(var7.exists()) {return "hf";}}return "";} catch (SecurityException var6) {log.log(Level.WARNING, "unable to detect ABI", var6);return "unknown";}}}

os代码如下:

private static String os() {String os = System.getProperty("os.name", "").toLowerCase();if(os.startsWith("linux")) {return "linux";} else if(os.startsWith("windows")) {return "win";} else if(!os.startsWith("mac os x") && !os.startsWith("darwin")) {if(os.startsWith("freebsd")) {return "freebsd";} else if(os.startsWith("android")) {return "android";} else if(os.startsWith("sunos")) {return "sun";} else if(os.startsWith("hp-ux")) {return "hpux";} else if(os.startsWith("kd")) {return "kd";} else {log.warning("unable to detect OS type: " + os);return "unknown";}} else {return "osx";}}

extension代码如下:

 private static String extension(String os) {return os.equals("win")?"dll":(os.equals("osx")?"jnilib":"so");}

这样,我就知道了dll的具体名字为什么是netlib-native_system-win-x86_64.dll了.

接下来,我还需要知道这个dll要放在哪里,才能被nd4j代码找到!

接下来,我们跳进JniLoader.load(natives.split(",")),看看load是如何执行的:

    public static synchronized void load(String... paths) {if(paths != null && paths.length != 0) {String[] javaLibPath = paths;int arr$ = paths.length;int len$;String path;for(len$ = 0; len$ < arr$; ++len$) {String i$ = javaLibPath[len$];path = (new File(i$)).getName();if(loaded.contains(path)) {log.info("already loaded " + i$);return;}}javaLibPath = System.getProperty("java.library.path").split(File.pathSeparator);//这里获得windows下path对应的路径,是个字符串数组String[] var11 = paths;len$ = paths.length;for(int var12 = 0; var12 < len$; ++var12) {path = var11[var12];log.config("JNI LIB = " + path);String[] extracted = javaLibPath;int len$1 = javaLibPath.length;for(int i$1 = 0; i$1 < len$1; ++i$1) {String libPath = extracted[i$1];File file = (new File(libPath, path)).getAbsoluteFile();//这里根据path路径和dll的名称拼成一个文件路径,下面检查这个文件是否存在,如果不存在,拼下一个文件路径,如果存在,就加载这个dlllog.finest("checking " + file);if(file.exists() && file.isFile() && liberalLoad(file, path)) {return;}}File var13 = extract(path);if(var13 != null && liberalLoad(var13, path)) {return;}}throw new ExceptionInInitializerError("unable to load from " + Arrays.toString(paths));} else {throw new ExceptionInInitializerError("invalid parameters");}}

其实这段代码本来我没有看太懂,但是架不住调试功能强大,运行一次,加断点,看看内容怎么变化,就知道是什么意思了.

这里假设我的windows下Path环境变量设置为:"D:\Python\Python35\Scripts\;D:\Program Files\Java\jdk1.7.0_15\bin;C:\WINDOWS;D:\BLAS;",那么javaLibPath的内容就会是这四个组成的字符串数组.接下来取出来第一个,和dll名称(netlib-native_system-win-x86_64.dll)一起拼成一个文件路径"D:\Python\Python35\Scripts\netlib-native_system-win-x86_64.dll",接下来检查这个路径是否是文件,如果不是,就会继续拼下一个,直到拼出"D:\BLAS\netlib-native_system-win-x86_64.dll",然后我的dll文件确实放在这里,程序就会加载.

现在我们找到对应的dll文件,这里为netlib-native_system-win-x86_64.dll,然后放在D:\BLAS\这个位置,把D:\BLAS加入path变量,然后重启Intellij(这一步不一定需要,大家自己尝试),让它重新读取path值.

重新运行下Test里的main函数,然后运行结果竟然还是如上的错误,额,是我们刚才的分析都是错误了么?

2.dll库的依赖和依赖查找

我们在跳进加载dll的地方,看看究竟是哪里错了:

private static boolean liberalLoad(File file, String name) {try {log.finest("attempting to load " + file);System.load(file.getAbsolutePath());log.info("successfully loaded " + file);loaded.add(name);return true;} catch (UnsatisfiedLinkError var6) {
            //这里捕获到一个异常,说Can't find dependent libraries.log.log(Level.FINE, "skipping load of " + file, var6);String tmpdir = System.getProperty("java.io.tmpdir");if(tmpdir != null && tmpdir.trim().length() > 2 && file.getAbsolutePath().startsWith(tmpdir)) {log.log(Level.FINE, "deleting " + file);try {file.delete();} catch (Exception var5) {log.info("failed to delete " + file);}}return false;} catch (SecurityException var7) {log.log(Level.INFO, "skipping load of " + file, var7);return false;} catch (Throwable var8) {throw new ExceptionInInitializerError(var8);}}

这里我们捕获到一个异常,说 D:\BLAS\netlib-native_system-win-x86_64.dll: Can't find dependent libraries.

原来如此我们的库还需要一些依赖库,这些依赖库没有找到.但是我们怎么知道这个库依赖于哪些库呢?哪些是已经有的,哪些是没有的?这里推荐一个工具,叫PEStudio,可以查看dll文件或exe文件依赖于哪些库:

这里显示了netlib-native_system-win-x86_64.dll依赖于哪些库,其中liblapack3.dll和libblas3.dll是我们需要找到了.其实到这一步,问题就算解决了,参见OpenBlas的官网,这些文件都可以很轻松的下载到,就是要注意首先需要知道你要的是32位还是64位,其次所有dll需要是统一的,不能部分32,部分64. 谢谢曲奇饼的提醒, 这里需要注意的还有jdk的版本, 在64位系统下, 应该使用64的jdk, 大家注意尝试.

这里是我用PEStudio找到的依赖关系:

至此,问题圆满解决.

=========================

最后,把在win10 64位系统下成功运行的库文件放在这:

csdn:http://download.csdn.net/detail/u201011221/9355487

baiduyun: http://pan.baidu.com/s/1jGO5waE

从源码看DL4J中Native BLAS的加载,以及配置相关推荐

  1. php源码自动识别文本中的链接,自动加载识别文件Auto.php

    用于本应用的控制器自动加载类设置,用法如同\CodeIgniter\Config\AutoloadConfig 自动加载识别文件:dayrui/App/应用目录/Config/Auto.php 语法格 ...

  2. Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)

    快速导航(持续更新中-) Cesium源码解析一(terrain文件的加载.解析与渲染全过程梳理) Cesium源码解析二(metadataAvailability的含义) Cesium源码解析三(m ...

  3. 从源码看ANDROID中SQLITE是怎么通过CURSORWINDOW读DB的

    更多内容在这里查看  https://ahangchen.gitbooks.io/windy-afternoon/content/ 执行QUERY 执行SQLiteDatabase类中query系列函 ...

  4. java 中 Native.loadLibrary 不能加载 jar 包中库文件的解决方案

    比如拿 jna 加载海康测速库文件为例: windows环境: (HCNetSDK) Native.loadLibrary("E:\\xxx\\HCNetSDK.dll", HCN ...

  5. Spring 源码分析(七)--bean的加载详细分析

    一:缓存中获取单例bean 前面已经提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再次尝试从sing ...

  6. ExoPlayer 源码阅读小记--HLS播放带缓存加载M38U文件过程

    基于ExoPlayer 2.17.1源码分析,基本是一边看一边写的流水账,记录下防止以后忘了: 第一步createMediaSource创建HlsMediaSource对象时同时会实例化出HlsPla ...

  7. CesiumJS 2022^ 源码解读[7] - 3DTiles 的请求、加载处理流程解析

    3DTiles 与 I3S 是竞争关系,可是比起生态开放性.数据定义的灵活性与易读性来说,3DTiles 比 I3S 好太多了.由于数据生产工具的开发者水平参差不齐,且数据并不存在极致的.万能的优化方 ...

  8. 【GStreamer源码分析】playbin播放test.wav加载插件过程分析

    playbin播放test.wav加载插件过程分析 一.前言 二.playbin 播放 .wav 音频插件加载一览 三.测试代码 3.1 gst_init 3.2 gst_element_set_st ...

  9. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

最新文章

  1. c 应用程序mysql_MySQL C 语言应用程序接口开发教程
  2. 一文读懂 Nginx
  3. 2-02字符编码的演化
  4. oracle安装要多久,一次耗时4天的oracle安装过程
  5. 刚刚开通blog写下几段文字,以表意义
  6. (12)verilog语言编写8路选择器
  7. 【算法】BloomFilter 与 CuckooFilter
  8. Python遍历破解FTP密码,并上传webshell
  9. 最全的iOS真机调试教程(证书生成等)
  10. win10无线网卡黄色感叹号
  11. PTA第八章7-2 统计英文单词个数
  12. 如何查看本地服务器名称
  13. 自动驾驶地图数据传输协议ADASISv3介绍
  14. Prometheus pod 流量监控
  15. 分布式自增ID雪花算法snowflake (Java版)
  16. 秋招冲刺:网络安全工程师入围成功之旅!!
  17. Yelp Dataset(Yelp业务-评论-用户数据集)
  18. C#文件操作 追加、拷贝、删除、移动文件、创建目录 修改文件名、文件夹名
  19. 使用 SSM 框架实现发送手机短信验证码
  20. void* shu_zu_ming[n][m] 传参的函数定义写法 【C++】

热门文章

  1. C# MethodInfo.Invoke 对应方法内部异常输出
  2. 达梦数据库的约束操作
  3. 胡须linux教程,一步步教你钩织hellokitty猫毛线拖鞋教程(儿童拖鞋)
  4. Kafka副本同步机制
  5. Houdini 学习文档 -VEX-VEX 编译器编译指示
  6. Windows资料之VS2010旗舰版安装
  7. Spark大数据分布式机器学习处理实战
  8. iframe隐藏滚动条
  9. MSDN Forum - SharePoint 自定义的自增长编号字段
  10. java dfa_JAVA 实现DFA算法