分类 翻车or坑 下的文章

翻车历史见这里

我胡汉三又回来了!!

编译的版本是Tensorflow 1.14。

为什么不用2?因为据说API大改,一堆坑。
像我这种改别人代码跑跑的肯定用不得

这次遇到的问题:

  1. 需要按照要求装MSYS,并且要使用MSYS2环境而不是MinGW环境(Github for Windows提供了MinGW,但是没有pacman);
  2. 记得适当修改bazel的缓存位置(真香),每次运行命令时使用选项--output_user_root,避免把缓存写在叠瓦技术(SMR)的机械硬盘上,那速度慢得爽的飞起;
  3. LLVM更新速度过快,而且现在已经更改项目名为llvm-project(之前是llvm),尽量使用和tensorflow发布时间相近的LLVM快照(时间过久会失效,但这不像Github的风格啊= =),在${PROJECT_ROOT}/tensorflow/workspace.bzl中可以找到并修改引用的LLVM版本;
  4. 截至Tensorflow 1.14和本文撰写时(2020/2/8),XLAnccl是不支持Windows平台或者支持的不是很好的(不折腾的话,几乎无法编译成功),不建议启用这两个特性;
  5. (某些特殊版本)需要修改${PROJECT_ROOT}/.bazelrc中,把build:cuda--define=using_cuda_nvcc=true注释掉,因为在Windows平台上不能使用--config=nonccl选项取消使用nccl,会报错;
  6. 有时候会遇到error C2993: 'Derived': 非类型模板参数 '__formal' 的不合法类型。在pytorch的issues里找到了类似的问题,说是由于依赖关系和多线程编译竞争带来的问题,设置为单线程编译--jobs 1可以解决问题(未测试):https://github.com/pytorch/pytorch/issues/25393

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

20200216补充

算了,放弃了,一直说是Eigen有问题,好像是关于GPU设备代码上找不到std::conj定义一类的问题。这几天网上冲浪的时候发现风评CUDA nvcc对于STL的支持很差,可能与这个有关。但是有dalao硬是编译出来了,就很不服气。在个人笔记本上编译特别浪费时间,1-2h起步,而且从来没成功过= =。等这波病情过去,借到好点的CPU资源再说吧。

主要起因是IPv6的出现吧。。现在大部分卖得火的便宜路由器往往会因为要么存储空间或RAM太小,或者CPU等硬件尚未得到支持而无法使用诸如OpenWRT一类的第三方固件。估计是出于商业目的吧,现在的路由器都不会宣称自己支持IPv6,到时候路由器就可以卖两道了。(然而放在移动放在学校寝室里的华为的接入点可以支持IPv6,估计要上千)然后接上自己的路由器,IPv6地址就没了,怪可惜的。因而打算同时支持IPv4+IPv6的“路由器”,学校接入点每次都要登陆,显然不行,主角就打算选这个刚买的开发板了。在我原本的想象中,这件事应该是十分简单的,计算机网络理论十分完整而清晰,再加上GNU这样的开源组织,多少也会提供一个能用的解决方案吧,毕竟路由器这玩意还是十分实用的。结果后来发现,著名的OpenWRT还是因为Cisco因为偷偷使用了Linux才不得不开源的。开源路由固件大头都要靠这种方式开头,这发展指望不得啊,唉。。。

现在的探索历程就很迷,在国内搜,估计基本tmd都是为了应付计网实验的,两台主机,静态ip,加个ip forward完事,要不就是vmware;在国外,也差不多只会实现流量转发的功能,不过写得比较实在,会在标题写上“流量转发”四个大字而不是像国内一样,不但话说不清楚,反倒觉得自己很NB了。看了一下鸟叔的Linux私房菜,不过里面关于路由器的那部分,层次有点高。。那真的是讲路由器的,不过是路由器之间的配置,上升到RIP和OSPF那个层次了。。我想要和下层设备的交互部分,DHCP啊。。

可能是没有找到正确博客的原因吧,在各大搜索引擎上零零散散地只是搜集到了一些相关的组件,比如Linux内核自带的IP转发IP forwarding功能,具有类似IP防火墙功能的iptables提供了网络地址转换NAT功能,hostapd提供的Wi-Fi热点功能,以及提供PPPoE拨号的pppoepppoeconf软件包(Ubuntu)。这虽然已经构成了路由器最基本的功能:数据的定向转发,但是离日常生活中花钱买的路由器还是有点距离的。除路由器的核心功能外,最关键的,也是最常用的一个,私以为便是DHCP了。

从stackoverflow上获得推荐的,ubuntu的官方路由搭建指导: https://help.ubuntu.com/community/Router

里面提到了一个DHCP服务器,不过Debian Buster的apt里面好像没有这个包: https://help.ubuntu.com/community/dhcp3-server

到时候有时间试试手动编译吧。

目前对于整个架构的设想: 使用开发板上的千兆网口作为网络输入,开发板上装Linux系统并进行有线网拨号;(已实现,pppoe) 插入无线网卡(RTL8188EU,已有内置驱动支持),作为Wi-Fi热点的发射装置;(已实现,hostapd) 使用虚拟网桥将有线网与Wi-Fi连接,让连入了Wi-Fi的设备可以手动配置后上网;(根据进一步的调查,发现这个可能要用iptables比较好,网桥的话,pppoe不知道能不能作为设备连接起来) 添加DHCP等路由器-终端自动配置服务,自动完成无线网络参数配置; 最后,如果可能的话,再更改一下Wi-Fi的认证机制,使其支持企业级认证。(记录一下,可能考虑用LEAP或者PEAP)

TensorFlow对于CUDA库的版本有着较为严格的要求,比如早期的TensorFlow1.10都不允许使用超过CUDA 9.0的版本。 好在现在版本放松了,允许使用指定范围内的CUDA进行编译。(然而还是有许多大神魔改编译配置,并且成功编译出了指定版本之外的环境组合,比如1.10+9.2) 但是随着TensorFlow在机器学习界的地位日益壮大,咕果公司也开始了夹带私货的行为。官方编译指导文档上,我们可以看见,从1.11.0开始,编译工具由cmake换成了自家的bazel

(个人观点) 在下认为,换个编译工具没问题,但起码Windows平台上要花点功夫。一个更加便捷的编译工具固然是好事,但是如果为使用该工具而附加的配置环境过于繁琐的话,不但不能吸引用户,甚至反过来还有劝退的效果。即便放开Windows平台不言,在Linux平台上的编译也有着不小的问题,仅仅是编译CPU版本的,不论是java平台,还是软件本身的问题,层出不穷。对自家的产品还有最高版本限制?这似乎是这个项目在于自家地位远远不够重要的表现,用户反倒要成为开发的主力军。 (bb结束)

编译的版本为1.13.2,为了使用cmake脚本,需要做以下改动:

  1. 添加tensorflow\core\util\version_info.cc,在改用bazel作为推荐生成工具之后,在对应位置下找不到该文件。但是文件内容经推断如下:

    /*  Generated by gen_git_source.py  */
    const char* tf_git_version() {return "b'1.13.2 Release'";}
    const char* tf_compiler_version() {return "1.13.2";}

    也可以从此看出,该文件由Python脚本自动生成,脚本位置位于tensorflow\tools\git\gen_git_source.py

  2. 编辑tensorflow\contrib\cmake\external\sqlite.cmake,由于在文章编写时链接已经失效,将18行改为
    set(sqlite_URL https://www.sqlite.org/2019/sqlite-amalgamation-3280000.zip)
  3. 若是使用了不低于第四代的Intel CPU,可以采用扩展指令集以提高性能。由于MSVC编译器只提供到SSE2 (2000)的支持,相比于受到支持、近年来开发的AVX2 (~2012)性能肯定不会优秀到哪里去,因而选择使用AVX2指令集进行编译。有的CMake只识别到AVX,需要到设定的编译目录中的CMakelists.txt中直接修改tensorflow_WIN_CPU_SIMD_OPTIONS对应的值为/arch:AVX2。(需要使用SSE4.2的可以使用Intel家的ICC编译器,自家CPU肯定有完美支持的)

正在编译中,已经出现了错误,等编译完后继续来补充

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

后续补充 编译失败了,好气。不想再编译这破玩意了,直接到github上预编译包得了。 https://github.com/fo40225/tensorflow-windows-wheel

没错,就是这么皮

原因还是很奇葩,电脑上装的是VS2013,不支持C++11标准,而dlib要求编译器支持C++11标准。但是由于宇宙第一IDE体态太大,重新安装费时费力,且不打算花时间处理可能存在的卸载残留,便打起了GCC的主意。在网上查阅资料,发现GCC4.7之后就支持C++11标准了,于是打算使用配置的MinGW-W64 GCC 6.4.0进行配置。

首先修改setup.py: 第148-157行,替换:

        if platform.system() == "Windows":
            cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
            if sys.maxsize > 2**32:
                cmake_args += ['-A', 'x64']
            # Do a parallel build
            build_args += ['--', '/m'] 
        else:
            cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
            # Do a parallel build
            build_args += ['--', '-j'+str(num_available_cpu_cores(2))]

        cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
        # Do a parallel build
        build_args += ['--', '-j4']

将MSVC的编译选项换为GCC的,MinGW-W64默认编译64位程序。 其中最后一行的-j4指的是make的线程数目,如何自由修改想必不必多说。 由于可以随时使用源码包的源文件重新替换,可以直接修改文件而不做注释。

若需修改所使用的数学包,修改dlib/cmake_utils/find_blas.cmake: 第67-68, 74行

   pkg_check_modules(BLAS_REFERENCE openblas)
   pkg_check_modules(LAPACK_REFERENCE lapack)
   ...
   set(REQUIRES_LIBS "${REQUIRES_LIBS} openblas lapack")

这里使用的是pkg-config作为依赖管理器,在Windows中可以通过使用环境变量PKG_CONFIG_PATH来配置其搜索路径。简单地来说,就是通过使用pkg-config工具,可以快速地获得在编译时引入该库时所需要的选项。 比如OpenBLAS库,假设安装位置在X:\openblas\,库文件位于X:\openblas\lib\,则在该目录下应有一个pkgconfig文件夹,里面有openblas.pc文件,为pkg-config所需要的库描述文件,在make时自动生成。(按理说所有的.pc文件应该放在统一的目录下,方便查找和缩短查找路径) 则此时配置PKG_CONFIG_PATH为:set %PKG_CONFIG_PATH%=X:\openblas\lib\pkgconfig;%PKG_CONFIG_PATH% .pc文件为文本文件,里面的路径空格需要特殊处理,可以使用\来转义连接,也可以用直接把路径用双引号括起来。

编译时需要指定cmake参数:

setup.py build -G "MinGW Makefiles"  --set "CMAKE_C_COMPILER"="C:/Program Files/mingw-w64/bin/gcc.exe" --set "CMAKE_CXX_COMPILER"="C:/Program Files/mingw-w64/bin/g++.exe" --set "CMAKE_MAKE_PROGRAM"="C:/Program Files/mingw-w64/bin/make.exe"

分别指定使用MinGW(而不是MSVC)、C编译器、C++编译器和make命令进行编译。

编译过程中可能遇到的问题:

  1. 编译过程中可能遇到hypot命名空间未定义的问题,这是由于Python将C++的hypot重命名为_hypot导致的。 解决方案为在Python安装目录下include/pyconfig.h中引入#include <cmath>加入所缺失的定义。 必须要将该头文件作为第一个include,因为C++引入和顺序有一定的关系。

  2. 安装依赖库(如BLAS库)的时候,路径带有空格,在最后的生成pyd的时候,可能会导致路径错误。 推荐解决办法是重新安装依赖库之不含空格等特殊符号的路径下。 但是如果实在是懒,不想重新编译安装(指自己)的话,在此提供一种骚操作: cmake将链接时所使用的库作为命令行,以文本的形式存储起来。在本例中,存储位置为build\temp.win-amd64-3.6\Release\CMakeFiles\dlib_python.dir\linklibs.rsp。 思想是通过修改纠正其中生成错误的地方,达到目标效果。 -lgfortran也要手动加入,如果使用了gcc编译的BLAS库的话 想法是美好的,但是现实是残酷的:在每次运行setup.py build时,为了确保代码的完整性,会将这些中间文件重新生成一遍。 也就是说,即使修改了该文件,再重新时,该文件的内容又会被重新以原方式生成一遍。 好在Windows 10对于cmd做了一些对图形界面更加友好的修改,使得我们可以不用再去先右键再选中文字了。在经过多次试验后,发现当选中文字时,不但不会继续刷信息,而且运行的程序似乎也会暂停运行(类似断点的效果?)。 也就是说,可以通过在运行到链接.pyd库之前的每一行输出时,暂停检查linklibs.rsp文件,直到出现该消息为止。 通过这种方式可以顺利通过编译,链接完以后,就基本完成dlib的编译了。 至于升级pkg-config是否能解决问题,在下并不知道。

===================================================== 后续更新

在使用的时候,报了 系统找不到指定文件 的错误。也就是说,最终整个过程还是失败的。 猜测可能的原因是,编译时生成的是libdlib.a文件,属于linux操作系统下的静态库,而加载dlib库时,应该要动态加载dll文件,因而产生了该问题。同样,也不能排除文件格式和后缀不同所导致的问题。其原因有待后续深入探究。当然,最好的办法还是按照官方教程来,使用合适版本的VC++编译器来完成功能。

  1. 先到github上拉一份发布版本的源码包下来
  2. 安装python, bazel(Google的开源编译工具), 以及gcc等编译环境必备工具 其中bazel的新版本在编译时会报错,根据stackoverflow上的建议,使用1.17.2版本的,可在其github的发布页面找到下载。
  3. python安装numpy包。
  4. ./configure
  5. bazel build --config=mkl --config=opt //tensorflow/tools/pip_package:build_pip_package 生成轮子,用于pip安装。由于打算使用intel的mkl进行加速,添加了--config=mkl的选项。不需要的可以删除。