0%

将你的科学计算从 Matlab 迁移到 Python?

为什么要用问句作为文章的标题呢,因为我的答案是 NO!Definitely not!

一切的一切起源于我看了一篇英文文章,说使用 Python 作科学计算有时比 Matlab 更加高效,还有那无数次见到的牛逼公式:,看的我心潮澎湃,我就寻思了,反正最近在做的那个算法用 Matlab 跑实验跑得不是太理想,要不咱也迁移一把,由于今天比较蛋疼,就决定开始尝试了,经过蛋疼的一天,我得出了开头那个结论:如果你有现成的 Matlab 算法,还是 Matlab 吧,如果你像我一样蛋疼,那就迁移吧。不相信,那就听我把遇到的一堆乱七八糟的事情娓娓道来吧,其中夹杂着我的一些弱弱的解决方案~~

.mat 数据集文件的导入

由于现有的数据集一般都是以 Matlab 的格式.mat 的形式出现的,Matlab 可以轻松的 load 语句搞定,但 Python 就不行了,花时间将 mat 文件提取成普通文本文件必然更心烦。其实这点 Python 肯定已经想到了,解决方案就是使用 Scipy 提供的函数,具体如下:

1
2
import scipy as sp
dataset = sp.io.matlab.mio.loadmat(dataset_name)

其中 datasetname 就是我们需要导入的.mat 文件,但问题又来了,导入后返回的 dataset 变量是一个“字典”的数据结构,它的 key 就是储存的变量名称,而对应的 value 就是变量的内容。这个操作并不像 Matlab 那样直接将.mat 里的变量载入 workspace,那我们当然还需要进一步使 dataset 中存储的变量暴露出来,对应的内容赋给对应的变量名称。可以查看 dataset 变量的组成,发现除了我们自己的变量,还有另外三个小东西:__globals____header__,`_version`,它们标识了.mat 文件的基本信息,但我们并不需要,所以还需要去掉它们。最终,我们通过一段代码实现:

1
2
3
4
exclude = ['__globals__','__header__','__version__']
for obj in dataset.keys():
if obj not in exclude:
exec(obj + ' = dataset[" ' + obj + ' "] ')

通过 exec 我们就实现了类似语句data = dataset["data"]的功能。OK,到这里,第一个任务完成,撒花~~可以看到,用 Matlab 一句 load 搞定的问题,这里搞了大半天。

迁移 everything

由于我搞的是代码迁移,即将原来 Matlab 的代码改成 Python 代码,本来还觉得迁移就是稍微改改,最后发现要迁移不是一点,而是 almost everything。首先一个最严重的问题:( )[ ]的问题,这恐怕是迁移过程中最频繁的一个改动,这是为什么呢?很简单,因为 Matlab 中取矩阵元素(即 Python 中所说的‘slice’切片操作)用的是小括号,而 Python 中用的是中括号,还有比这更 fuck 的事情吗,因为函数调用也是小括号,所以放弃查找替换这不切实际的念头吧,这个恐怕只能手动。别以为这样就完事了,更琐碎的东西等着你,下面的表格可以帮助你理解什么是我说的 everything。

操作 Matlab Python
注释 % #
开始索引 1 0
矩阵连接 [a, b] numpy.concatenate(a, b)
循环和分支语句 未结束的条件行以,结尾结束需要end 未结束的条件行以:结尾结束不需要任何东西
产生全 0 全 1 矩阵 zeros(m, n)ones(m, n) numpy.zeros((m, n))numpy.ones((m, n))
整除问题 / 直接取准确结果,不存在整除问题 / 两遍都是整数时为整除,分子或分母需要加 float 强制转换
乘方 a^b a**b
矩阵向量转置 单引号 ' 搞定 没有重载 ' 操作符,需要调用 .transpose.T
结构体数组 直接{ }搞定 { } 指的是字典数据结构,没有结构体的概念,只能使用“对象列表”搞定
向量矩阵相乘 a*b 代表正常的矩阵相乘,也就是说 a 的列数必须和 b 的行数匹配的那种 a*b 代表对应元素相乘,即 elementwise,a 和 b 的维数必须相等
a.*b代表对应元素相乘,即 elementwise,a 和 b 的维数必须相等 dot(a, b)代表正常的矩阵相乘,也就是说 a 的列数必须和 b 的行数匹配的那种

OK,这么些个问题需要解决,有些还是可以接受的,毕竟两个不同的语言嘛,操作符什么的不一样还行,有的就比较恶心了。其中最让人难以理解的就是 numpy 的 zeros 和 ones 不明白为啥参数一定要是一个“元组”,不知道当初创造这个时怎么想的,搞的加了两层括号。而最变态的就是矩阵向量的相乘了,Matlab 分的很清,单独的 * 号就是矩阵相乘,而 .* 就是对应元素相乘,而 Python 中单独的 * 号表示元素相乘,作用与 Matlab 恰恰相反,真正的矩阵乘法居然需要调用dot函数才能完成,而dot只接受两个参数,可想而知,一堆矩阵相乘的时候得多壮观啊,譬如:dot(a, dot(b, dot(c, dot(d, e) ) ) ) ,类似的变态事还有矩阵连接,本来 Matlab 两矩阵放一起就能连接,Numpy 非要用一个函数,你用函数我忍了,你还用那么长一个函数concatenate,要是连接几个矩阵咋办,一行能写下吗?大哥,我彻底凌乱了~

后话

鉴于上述的种种让人闹心的原因,我的移植工作没有坚持下去。当然,我写下这篇文章,并没有诋毁 Python 在做科学计算这方面的潜力,更没有贬低 Python 的意思,不得不承认接触 Python 越多,越觉得它牛逼,我仅仅表达的是要从 Matlab 迁移到 Python 有点得不偿失,或许一开始就直接从 Python 开始是个不错的选择,不存在迁移,直接从头开始。但如果你想我一样,有现成的 Matlab 代码了,那咱就老实 Matlab 吧~~