最近在好几个地方用到了KNN这个最简单的分类算法。KNN虽然原理和实现都相当简单(当
然是最基础的形式),但在某些问题上效果也好得出奇。缺点当然也非常明显,至少速度
在数据量稍微大一点的时候就变得很慢了。所幸的是在做prediction的时候,sklearn的实
现中是可以允许通过n_jobs
参数来实现并行的。不过在最近使用一个KNN的案例中遇到了
一个问题,当使用自定义的距离函数时,并行似乎没有任何效果。
其实似乎sklearn中类似的小坑还是比较多的,尽管很多并不一定是sklearn本身的问题 。比如用RandomizedCV时,如果
n_jobs=1
,此时定义verbose
参数就是无效的,会在 jupyter中输出一堆中间结果。
考虑到当KNN采用暴力遍历距离矩阵时,其实预测每一个点的任务是独立的,那么其实自己 很容易就可以写一个wrapper来让predict函数用多进程实现并行:
先写一个函数可以把job拆分:
def split_chunks(l, n):
chunk_size = math.ceil(len(l) / n)
for i in range(0, len(l), chunk_size):
yield l[i:i + chunk_size]
然后其实只要再写一个简单的函数就可以了:
def parallel_predict(inp, n_procs=4):
job_list = list(split_chunks(inp, n_procs))
with multiprocessing.Pool(n_procs) as pool:
res = pool.map(clf.predict, job_list)
return np.concatenate(res)
从我自己的数据集来看,并行的overhead还是比较小的,加速效果明显。
标题里还提到了关于时间序列的分类问题,这个其实是与上面问题的应用背景有关的。对 于时间序列相关的机器学习问题,传统上有一种方法是从时间序列里面提取特征(比如各 种统计特征、频谱特征等),然后再将特征输入给机器学习模型。这种特征提取的工具已 经有一些开源的产品,典型的代表是Matlab的HCTSA和Python的tsfresh,我自己也实现了 一个类似于tsfresh但在特征描述能力和计算速度上都有大幅度提升的库。
之所以会想起来这些,是因为最近知道了有同事用一种复杂的算子做了一些时间序列的工 作,其中一个实例就是用UCI的human activity数据集来验证其效果。从展示的结果来看, 似乎还有提高的空间,因此出于好奇,想试试看自己写的库提出特征效果如何。不试不知 道,一试之后结果大失所望,效果非常的差。回过头又试了一下tsfresh,不出意料,比我 的结果还要更差。可惜手上并没有HCTSA,不然也很有兴趣试一下它的效果如何,我的预期 是不会好太多。
后来想到其实这类问题应该可以用其它方法做,比如用类似于dynamic time warping的方 法来定义时间序列的距离,然后直接用KNN来做分类。随便定义了一些参数,然后解决了上 面的速度问题,发现果然效果好了很多。