比较gzip压缩和解压缩性能

Benchmark gzip compression and decompression performance

gzip是很多测序数据的默认压缩格式。在学校HPC环境中,几个GB的文件的压缩和解压缩往往需要几分钟到几十分钟的时间。如果处理的数据较大,gzip的压缩和解压缩会占用不少时间。因此我想看看有没有比Linux默认的gzip更有效率的软件。这里主要比较:

  1. libdeflate 这是samtools的选择
  2. igzip 这包含在Intel ISA-L软件中
  3. pigz 这是gzip的多线程实现

具体的测试分为压缩和解压缩两部分。用的是一个FASTQ文件,压缩后大约2G,原始文件6G。

测试结果如下:

Decompression
User Time (s)Real Time (s)Memory
gzip39.73u1.47s41.24r876kB
igzip7.60u1.81s11.31r2844kB
libdeflate20.45u6.53s28.05r7909292kB
pigz30.18u4.86s23.49r1000kB
Decompression Benchmark
Compression
User Time (s)Real Time (s)MemoryCompressed File Size
gzip620.17u2.54s623.23r1116kB1.81G
igzip15.61u1.92s17.69r2628kB2.03G
libdeflate152.88u10.29s163.43r7879072kB1.78G
pigz995.38u4.15s18.18r38352kB1.82G
Compression Benchmark

结论:Intel igzip在耗时指标中完胜。在默认设置下,igzip解压缩时间是系统提供gzip的1/5,压缩时间是1/40。唯一不足的是压缩后的文件偏大。

讨论:

这个结果和samtools 的benchmark差别很大。我发现samtools用的GitHub仓库(https://github.com/jtkukunas/zlib)不同于Intel Tuning Guide里提供的仓库(https://github.com/intel/isa-l/blob/master/README.md)。 因此性能的差别可能来源于samtools没有用官方的实现,当然也有可能是测试方法不同:samtools测量的是bam文件的读写速度,而我只是用FASTQ文件作为测试。

其他相关的gzip工具还有zlib-ng 和cloudflare优化的zlib。这些性能应该在igzip和系统自带的gzip之间,这里就不再测试了。

解决一个奇怪的网络连接错误

解决一个奇怪的网络连接错误
Solve a strange network problem

我的服务器bunny最近出现了一个奇怪的网络连接错误:只有台式机和服务器能连接到bunny,笔记本就连不上。
假设网络IP如下:

A. 服务器bunny:198.215.54.48 10G 网络
B. 服务器: 198.215.54.5 10G 网络
C. 服务器bronco: 129.112.7.169 1G 网络
D. 台式机:129.112.185.246 局域网
E. 笔记本:172.17.157.121 无线网

现在的问题是可以从B, C, D 连接到A,但不能从E连接到A。

解决思路如下:

1. 怀疑E->A的路由有问题

通过traceroute,发现E到A的最后一跳是*,状态是Host unavailable.
但进一步发现E->B,A和B在同一个子网,因此E->A的路由不大可能有问题

2. 检查A的防火墙设置

先关闭所有防火墙,用:

ufw disable

但E仍然不能连A

3. 检查A的路由

> route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 198.215.54.254 0.0.0.0 UG 0 0 0 em1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 em4
198.215.54.0 0.0.0.0 255.255.255.0 U 0 0 0 em1

> ip route --list
ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default

仔细检查发现172.17.0.0这个路由有问题:因为E在172.17.0.0网络,从E发出的ICMP包到达A,而从A返回的ICMP包没有通过em1接口而是docker0发出,这样E->A就显示了Host unavailable。

值得注意的是在路由IP包时,它的终点并不是第一个可以匹配的路由,而是所有路由里的第一个匹配最好的路由。
原文:Any entry whose first field matches the destination IP address completely(a host) or partially (a network) would signal the IP address of the next router. (link)

找到原因后,我们可以让docker0的网络IP地址避开172.17.0.0子网,这就可以解决E->A的连接问题。
这个解决方案里的重要命令是 (参考链接


ip link set dev docker0 down
ip addr del 172.17.42.1/16 dev docker0 # 172.17.42.1是docker0的ip
ip addr add 10.0.0.10/24 dev docker0
ip link set dev docker0 up
ip addr show docker0 #检查docker0的ip

此外要在/etc/default/docker里加上DOCKER_OPTS (–bip参数见Docker文档Customize the docker0 bridge)。这样docker0的IP不会在服务器重启后改变。


DOCKER_OPTS="--bip=10.0.0.10/24"

题外话

1. 为什么docker和无线网都用172.17.0.0子网?

根据RFC 1918 3. Private Address Space, 保留IP除了常见的192.168.0.0外, 还有:

10.0.0.0 - 10.255.255.255 (10/8 prefix)
172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
192.168.0.0 - 192.168.255.255 (192.168/16 prefix)

因为172.17.0.0是保留地址,正巧docker和无线网同时都使用了这个子网,因此造成了我遇到的网络问题。

2. 怎么监测服务器是否接收到IP包?

可以使用iptables

例如参考 http://askubuntu.com/questions/348439/iptables-log-file-and-how-change-it, 记录192.168.11.0/24发来的包:


iptables -A INPUT -s 192.168.11.0/24 -j LOG --log-prefix='[netfilter] '

可以在/var/log/kern.log 找含有“[netfilter]”的日志。

如果想监测ICMP包,可以用:


iptables -A INPUT -p icmp -j LOG --log-prefix='[netfilter] '

其他常用命令(参考这个链接)包括:


sudo iptables -L # 打印iptables内容
sudo iptables -L INPUT -v # 检查INPUT表收到了多少数据
sudo iptables -Z # 清零
sudo iptables -Z INPUT # 清零INPUT表数据清零
sudo iptables -A INPUT -p icmp -j LOG --log-prefix='[netfilter] ' # 记录ICMP包
sudo iptables -D INPUT -p icmp -j LOG --log-prefix='[netfilter] ' # 删除防火墙规则(记录ICMP包)
sudo iptables -F #防火墙规则生效

以下命令重置防火墙

sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -F
sudo iptables -X

iptable是个复杂的防火墙,它使用了table,chain等概念,比如下图:

iptables Overview
iptables Overview

(来自链接
更详细的介绍可以参考[2]。

除iptables外,Ubuntu还提供了UFW,这个工具建立在iptables上,提供比iptables更简易的配置。比如:


ufw enable # 启用防火墙
ufw disable # 关闭防火墙
sudo ufw status # 显示防火墙设置
ufw deny 80/tcp # 封锁tcp的80端口
sudo ufw delete deny 80/tcp # 删除上一条(封锁tcp的80端口)

更多的信息可以参考Ubuntu UFW文档 (链接)。

 

参考

[1]7 Linux Route Command Examples (How to Add Route in Linux)

[2]Linux Firewall Tutorial: IPTables Tables, Chains, Rules Fundamentals

建立SFTP共享账户

建立SFTP共享账户
Create SFTP share account

做学术的时候往往需要交流数据。目前最安全简便的解决方案是用SFTP传输文件协议。
为此,需要配置Linux服务器,并为学术伙伴建立一个不能登录、只能用SFTP的账号。

下面是步骤:

1. 建立用户组

addgroup --system filetransfer

2. 设定用户目录和相应权限
以用户名username为例:

mkdir /home/username
mkdir /home/username/data
chown root:root /home/username
chown username:filetransfer /home/username/data
chmod 755 /home/username
chmod 700 /home/username/data

权限755可以允许sftp用户登录到其HOME目录/home/username
权限700可以防止上传的数据被其他用户访问

3. 建立用户

useradd -d /home/username -s /usr/lib/sftp-server -M -N -g filetransfer username

-d 指定HOME目录
-s 指定登录shell
-M 不创建HOME目录
-N 不创建以username为名的 用户组
-g 指定user group

4. 配置ssh

打开 /etc/ssh/sshd_config

见到这行之后:Subsystem sftp /usr/lib/openssh/sftp-server
改称:Subsystem sftp internal-sftp
然后在末尾加上:

Match group filetransfer
ChrootDirectory %h
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp

5. 配置登录shell

在/etc/shells末尾加上一行:

/usr/lib/sftp-server

6. 最后重启ssh 服务器就好了

service ssh restart

不要用C++11特性开发R包

不要用C++11特性开发R包
Don’t use C++11 features to develop R packages

这是个教训。在我开发seqminer的时候浪费太多时间了。希望其他人不要重复我的错误。

C++11有不少有意思的特性,但为什么不要在开发R包的时候使用C++11的特性呢(这里指R 3.2)?下面有一些原因。

1. 没有清晰的文档
R对于C++11的支持写的很模糊。你需要看两个文档,Wring R ExtensionsR Installation and Administration
第一个文档提到你需要CXX_STD,第二个文档提到CXX1XFLAGS。这时候你知道应该设置一些变量,但怎么设置?没有文档告诉你。

这里我找到一个方案,可以凑合用。你需要改configure.ac文件。不过你仍会碰到下面提到的第3个问题。

dnl https://github.com/cran/dfcomb/blob/master/configure.ac
dnl copy the following lines to configure.ac

AC_DEFUN([AX_GET_R_CONF_VAR],[$1=`"${R_HOME}/bin/R" CMD config $1`])
AX_GET_R_CONF_VAR([CC])
AX_GET_R_CONF_VAR([CFLAGS])
AX_GET_R_CONF_VAR([CPPFLAGS])
AX_GET_R_CONF_VAR([CXXCPP])
AX_GET_R_CONF_VAR([CXX1X])
AX_GET_R_CONF_VAR([CXX1XSTD])
AX_GET_R_CONF_VAR([CXX1XFLAGS])
AX_GET_R_CONF_VAR([CXX1XXPICFLAGS])

CXX="${CXX1X} ${CXX1XSTD}"
CXXFLAGS="${CXX1XFLAGS} ${CXX1XPICFLAGS}"
CXXPICFLAGS="${CXX1XXPICFLAGS}"

2. 跨平台支持很艰难

当你把包提交到CRAN时,很有可能在Solaris系统上出现编译或运行的错误。
如果你使用了C++11的功能,你可能想在自己安装Solaris试试。
从我的经验来看,Solaris11 + Solaris Studio 12.4环境比较好搭建。
但CRAN上用的是很老的Solaris10 + Solaris Studio 12.3。
如果你想搭建这个系统,你需要从源代码编译R,然后你会发现,编译不了!
我发现blas库无法被链接。因此你在提交R包到CRAN时,最好明确说,我不支持Solaris。

3. 支持Linux系统也会出问题

在Linux系统上,也许gcc的版本很新,可以支持C++11,但如果安装R的时候没有设置C++11需要的一些变量,你还是用不了。
比如Ubuntu系统里, R是用apt安装的,这个版本的R是默认不知道你有没有支持C++11的编译器的。
这就是说,你在开发时使用的R,CRAN上的R都可以安装你的包,但不保证你的用户能安装。

怎么知道你的R是否支持C++11呢?你可以运行“R CMD config”:

  CXX1X         C++ compiler command for C++11 code
  CXX1XSTD      flag used to enable C++11 support
  CXX1XFLAGS    C++11 compiler flags
  CXX1XXPICFLAGS
                special flags for compiling C++11 code to be turned into
                a shared library

然后看一下CXX1X的值, “R CMD config CXX1X”,如果这个值是空的。默认情况下没法编译安装你写的R包(install.packages())。
顺便说一下: CXX1XXPICFLGS是一个笔误,正确的拼写是CXX1XPICFLAGS (不是两个XX)

解决方法:
先在shell下:

export CXX1X=”g++ -std=c++11″
export SHLIB_CXX1XLD=”g++ -std=c++11 -shared”

然后R里面就能用install.packages()安装了。
顺便说一下,SHLIB_CXX1XLD 没有写在手册里……

现在总结一下,如果你用了C++11的特性写R包,你会花很多时间读手册让R去选用合适的编译器,很难去支持CRAN上的老旧的Solaris,不能保证最终用户能够编译安装你的R包。
所以,我的建议的是,如果想让更多的人用你的R包,别折腾C++11的特性去适应R,还是写最portable的C++代码吧。

安装GCC 4.9

安装GCC
Install GCC

这是我找到的最简单的安装gcc,并且不需要root权限的方法 (https://gcc.gnu.org/wiki/InstallingGCC

这个方法适用与GCC的多个版本(4.6,4.7,4.8,4.9)

tar xzf gcc-4.6.2.tar.gz
cd gcc-4.6.2
./contrib/download_prerequisites
cd ..
mkdir objdir
cd objdir
$PWD/../gcc-4.6.2/configure --prefix=$HOME/gcc-4.6.2
make
make install

第七行:如果只需要64位编译器,不需要32位的,可以加上–disable-multilib
第八行:可以用-j N参数指定N个进程同时编译

设置代理服务器

设置代理服务器
Setting up proxy

出于对患者信息的保护,学校内网接入Internet时必须使用代理(Proxy)。
这令很多软件的安装使用过程变得复杂。

这个帖子会列出给常用软件设置代理的方法。

R

最直接的设置代理的方法是设置环境变量: http_proxy, https_proxy, 例如:

Sys.setenv(http_proxy="http://proxy.swmed.edu:3128")
Sys.setenv(https_proxy="http://proxy.swmed.edu:3128")

如果上述方法不起作用,也有可能需要:

# In R, a partial solution is to use:
options(download.file.method="wget")

这是在curl不能正常使用代理,但wget可以用的时候的解决方案。

还有一种情况是用devtools安装R的扩展包,可以用:

# For devtools
library(httr)
set_config(
  use_proxy(url="proxy.swmed.edu", port=3128)
)

如果Mac电脑上没有安装X11,装package时需要指定repository(来源),比如:

install.packages('RMySQL', repos='http://cran.us.r-project.org')

Docker

很多软件提供了Dockerfile。用户需要使用docker build来制作自己的容器(docker)。
如果在防火墙后面, 简单的方法是用(link):

docker build 
  --build-arg https_proxy=$HTTP_PROXY --build-arg http_proxy=$HTTP_PROXY 
  --build-arg HTTP_PROXY=$HTTP_PROXY --build-arg HTTPS_PROXY=$HTTP_PROXY 
  --build-arg NO_PROXY=$NO_PROXY  --build-arg no_proxy=$NO_PROXY -t java .

另外一个方法是手动修改Dockerfile,加入ENV语句,例如:

ENV HTTP_PROXY http://proxy.swmed.edu
ENV HTTPS_PROXY http://proxy.swmed.edu
ENV http_proxy http://proxy.swmed.edu
ENV https_proxy http://proxy.swmed.edu
....

R package的UBSAN测试

R package的UBSAN测试
How to perform UBSAN tests on R packages

最近需要提交一个R package (seqminer)到CRAN,我们需要保证这个包代码通过UBSAN 测试(Undefined bahavior sanitizer tests,详见这个链接).
这个测试可以有效的检测结果不确定的指令(例如打印INT_MAX+1)。

目前只有Clang(版本3.3以后)才能支持UBSAN。为了对R package进行UBSAN测试,我们首先需要一个特殊的R版本,即用打开UBSAN支持(-fsanitize=undefined)的Clang编译的R。参考了网上一篇Blog,写一下简单步骤:

1. 下载编译工具

sudo apt-get install valgrind subversion r-base-dev clang-3.4 texlive-fonts-extra texlive-latex-extra
sudo apt-get build-dep cran-base

2. 下载R源代码(devel版本)

svn co https://svn.r-project.org/R/trunk ~/R-devel

3. 替换R-devel/config.site

CC="clang -std=gnu99 -fsanitize=undefined"
CFLAGS="-fno-omit-frame-pointer -Wall -pedantic -mtune=native"
F77="gfortran"
LIBnn="lib64"
LDFLAGS="-L/usr/local/lib64 -L/usr/local/lib"
CXX="clang++ -std=c++11 -fsanitize=undefined"
CXXFLAGS="-fno-omit-frame-pointer -Wall -pedantic -mtune=native"
FC=${F77}

4. 编译

cd R-devel
./configure --with-x=no --without-recommended-packages
make

这里–without-recommended-packages会省略一些R的packages(例如boot,nlme,Matrix) 以提升编译速度。如果不用这个选项,我发现Matrix package总是没法编译

5. 建立~/.R/Makevars

CC = clang -std=gnu99 -fsanitize=undefined -fno-omit-frame-pointer
CXX = clang++ -fsanitize=undefined -fno-omit-frame-pointer
PKG_LIBS = /usr/lib/llvm-3.4/lib/clang/3.4/lib/linux/libclang_rt.ubsan_cxx-x86_64.a

这里CXX 一定要用clang++,如果用clang的话会出现链接错误,比如找不到这个符号: _ZTVN10__cxxabiv117__class_type_infoE

这里还需要指定PKG_LIBS包括LLVM的库文件,不然就找不到UBSAN的符号。

6. 测试package

R CMD build seqminer/
R CMD check seqminer_2.7.tar.gz

UBSAN测试的结果会显示在:seqminer.Rcheck/tests/tests.Rout
如果有错误,你会看很长的一行, runtime error….

其他技巧

检测内存还可以用valgrind:

R CMD check --use-valgrind seqminer_2.7.tar.gz
R -d valgrind --vanilla < mypkg-Ex.R
R -d "valgrind --tool=memcheck --leak-check=full" --vanilla < mypkg-Ex.R

Create dynamics DNS

自动更新IP
Create dynamics DNS

办公室电脑的IP经常变动。为了能远程登录,我需要知道它的IP是什么。
我的解决方法是写一个小程序,这个程序把办公室电脑的IP绑定到域名 zhanxw.x64.me
然后,我让办公室电脑每小时运行这个脚本一次,这样zhanxw.x64.me就能指向办公室电脑了。

这里x64.me域名是由DNSDynamic (www.dnsdynamic.org)提供的.

下面是脚本,注意使用的时候要替换username和password。

#!/usr/bin/env python
import socket
ip=str(socket.gethostbyname(socket.gethostname()))
print ip

import urllib2, base64
request = urllib2.Request("http://www.dnsdynamic.org/api/?hostname=zhanxw.x64.me&myip=%s" % ip)
username = "######"
password = "######"
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
request.add_header("Authorization", "Basic %s" % base64string)   
result = urllib2.urlopen(request)

data = result.read()
print data

对于Windows主机,可以用计划任务每小时运行脚本一次。对于Linux主机,可以用crontab。

gcc参数顺序

gcc参数顺序
Ordering of gcc parameters matters

2013年最后一个月最后一个星期谈一个奇怪的bug,我在尝试pinfo的时候,configure脚本会提示ncurses有问题:

checking location of curses.h file... /usr/include/ncurses.h
checking if curses is usable... no
configure: error: Curses not found. You need curses to compile pinfo

打开config.log文件,发现这个提示:

configure:12563: checking if curses is usable
configure:12587: gcc -o conftest -g -O2 -I/usr/include    -L/usr/lib -lncurses conftest.c  >&5
/tmp/ccBLkSqd.o: In function `main':
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:38: undefined reference to `initscr'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:39: undefined reference to `printw'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:40: undefined reference to `stdscr'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:40: undefined reference to `wrefresh'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:41: undefined reference to `stdscr'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:41: undefined reference to `wgetch'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:42: undefined reference to `endwin'
collect2: ld returned 1 exit status

从这个命令行来看,curses库的头文件目录和库文件目录都对,但编译的时候(链接/tmp/ccBLkSqd.o)却找不到几个函数的定义(比如:initscr,printw等等)。为了重现这个看起来不该出现的问题,我把conftest.c创建好,然后用相同的gcc命令行编译代码,的确是可以重现错误。

更令我不解的是如果把conftest.c放到命令行的末尾,这个编译就能通过了:

# This does not work
gcc -o conftest -g -O2 -I/usr/include    -L/usr/lib -lncurses conftest.c 
# This works
gcc -o conftest conftest.c -g -O2 -I/usr/include    -L/usr/lib -lncurses  

为什么把conftest.c移到“-L/usr/lib -lncurses”前面就能解决问题?
看到StackOverFlow上的一个帖子

这个帖子讲到gcc编译和链接一个源程序时,会保留参数的顺序,也就是说在链接时,把conftest.c放到前面和后面,效果不同。具体来讲:

conftest.c 放到后面

gcc -o conftest -g -O2 -I/usr/include    -L/usr/lib -lncurses conftest.c 
<=> 等价于
gcc -o conftest -g -O2 -L/usr/lib -lncurses tmporary_object_file.o

conftest.c 放到前面

gcc -o conftest -g -O2 -I/usr/include conftest.c -L/usr/lib -lncurses 
<=> 等价于
gcc -o conftest -g -O2 tmporary_object_file.o -L/usr/lib -lncurses 

也就是说,gcc在链接目标文件时,会依照命令行的顺序,对于每个在前面目标文件中没有定义的函数,都试着在其后面的目标文件找找,如果最后一个目标文件也找不到,就报错。因此在我们的例子里,tmporary_object_file.o一定要放到-L/usr/lib -lncurses前面才能正确编译。

回到本文最开始,我们如何让configure把conftest.c放到“-L”前面?答案不是改动configure,而是使用LIBS环境变量:

LIBS="-L/usr/lib -lncurses" ./configure

与LIBS不同,LDFLAGS会放到conftest.c前面,这样configure还是没法正确的使用configure。

在本文最后,回想我们本来的问题,就是说gcc的参数有一定的顺序要求。也许gcc的手册里写了顺序的要求,但是很少有人能够看完gcc的手册。如果见到类似问题,应该怎么办呢?我觉得可以有两个思路,第一个就是想想写gcc软件的人是怎么想的。比如现在gcc的选择就是一个很方便的方法,如果当前没某个函数没有定义,就在他后面制定的目标文件里找找。如果这个要求反过来,或者任何顺序都支持,那么链接的函数就会复杂(因为链接程序需要存储所有定义过的函数,参见链接);第二个就是想想传统的(或者标准的)写法是什么,把已知的经验和这个错误联系起来。比如configure的错误看起来很奇怪,但是先按照传统的写法先编译后链接,找到一个能凑合工作的方案(把conftest.c放到前面),再逐步改变到出错时的命令行,这个逐步的过程可以帮助查错也能巩固一下已有的知识。

安装Theano

安装Theano
Install Theano

Theano是一个Machine Learning的工具箱,因为和Deep Learning相关,现在非常流行。

这篇文章简单记录一下安装Theano是遇到的问题。

1. import theano出错

>>> import theano
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/__init__.py", line 76, in <module>
    import theano.tests
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/tests/__init__.py", line 6, in <module>
    import unittest_tools
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/tests/unittest_tools.py", line 8, in <module>
    import theano.tensor as T
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/tensor/__init__.py", line 10, in <module>
    import blas
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/tensor/blas.py", line 393, in <module>
    StrParam(default_blas_ldflags()))
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/tensor/blas.py", line 332, in default_blas_ldflags
    blas_info = numpy.distutils.__config__.blas_opt_info
AttributeError: 'module' object has no attribute '__config__'

加入下面几行可以解决问题:

import numpy
import numpy.distutils
import numpy.distutils.__config__

2.Theano无法编译

运行示例程序是遇到编译错误:

Traceback (most recent call last):
  File "logistic_sgd.py", line 373, in <module>
    sgd_optimization_mnist()
  File "logistic_sgd.py", line 271, in sgd_optimization_mnist
    y: test_set_y[index * batch_size: (index + 1) * batch_size]})
...
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/gof/cmodule.py", line 1641, in compile_str
    return dlimport(lib_filename)
  File "/net/fantasia/home/zhanxw/python/lib/python2.7/site-packages/Theano-0.6.0rc3-py2.7.egg/theano/gof/cmodule.py", line 263, in dlimport
    rval = __import__(module_name, {}, {}, [module_name])
ImportError: ('/net/fantasia/home/zhanxw/.theano/compiledir_Linux-3.4.61-x86_64-with-debian-wheezy-sid-x86_64-2.7.5/tmphSYif1/baf8a6fdee34b135ea9110785e356489.so: undefined symbol: ATL_dptsyrk', '[_dot22(<TensorType(float64, matrix)>, W)]')

这里的提示信息是找不到ATL_dptsyrk函数。在没有管理员权限的服务器上,Theano找不到合适的ATLAS函数库。
解决方法是加入以下几行到 $HOME/.theanorc

[blas]
ldflags = -lf77blas -latlas -lgfortran

对于其他错误来讲,还有一些更一般的解决方法。

1. 可以打开Theano的日志信息,比如在程序中加入这两行:

import logging                                                                  |
#logging.getLogger("theano.gof.cmodule").setLevel(logging.DEBUG)

Theano在运行是可以输出更多的信息。

2. 保留临时文件
可以在 $HOME/.theanorc 中加入以下两行

[global]
nocleanup = False

这样可以保留Theano编译过程中的临时文件,便于调试。

题外话

在有管理员权限的Ubuntu系统, 可以用OpenBLAS来代替ATLAS,因为OpenBLAS是多线程的,而且速度更快。具体可以参考Theano 的手册

现在Theano可以正常使用,希望接下来几篇blog有空能写写学习Deep Learning的心得。