Cython 可以通过强类型 大大加快Python代码的运行效率:
简单例子:
pure python
Cython:
将上述代码用Setup编译成Python包后 调用执行,
当使用pure Python 时耗时4.5s Cython耗时0.96s
这里cdef 是c相应类型的定义
这里还提供了对于异常的问题,当定义强类型函数时,一般需要指定exception,
值 当返回相应的exception值时说明产生了异常,否则Cython对于特殊的cdef返回值函数
是不具备异常提醒机制的。
Cython 用于提升np.ndarray相关的性能的方法在于指定数据类型
cdef np.ndarray 及对ndarray下标访问时 如指定下标cdef int i
应该使用转型<unsigned int> i 代替i进行下标访问。
使用Cython 可以大大加快Python代码运行
这一般限于对于底层的一些C类型,而不是所有情况,例如对于一般的np.ndarray
这种已经使用C进行了较大优化的数组,这种情况是有限的,
而且对于诸如一些for循环, 当循环次数不够多时,Cython的加速也是捉襟见肘。
而且由于引入了新的类型及变量会导致运行时间更长,
有时候速度与内存是不可兼得的,
比如要使用一个Bagging算法(统计中的BootStrap)需要初始化抽样数组,
这时如果利用生成器对于每一个样本逐个生成,并进行后续处理,
会在占用较小内存的情况下完成任务,但速度不佳,
但也可以选择先生成所有抽样下标样本,这样会消耗较多的内存,但速度应当是更快的。
举一个例子,下面的代码是用来产生Bagging样本的pure python代码:
这些函数的作用仅仅是生成样本及计算特征值的比,但当考虑性能采用Cython进行改进
会得到一个非常不具有可读性的版本:
在测试这两个程序时会发现,Cython比前者慢(不会慢太多),这与直接将前者进行Cython编译
的结果是近乎相同的,当数据量不够大时后者完败。
所以Bagging的性能消耗本质仍是内存与速度的取舍,而非编译语言。
简单例子:
pure python
def f(x):
return x ** 2 - x
def intergrate_f(a, b, N):
s = 0
dx = (b - a)/ N
for i in range(N):
s += f(a + i * dx)
return s * dx
print intergrate_f(-2.5, 10.5, 10000000)
Cython:
def f(double x):
return x ** 2 - x
def intergrate_f(double a, double b, int N):
cdef int i
cdef double s, dx
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a + i * dx)
return s * dx
将上述代码用Setup编译成Python包后 调用执行,
当使用pure Python 时耗时4.5s Cython耗时0.96s
这里cdef 是c相应类型的定义
这里还提供了对于异常的问题,当定义强类型函数时,一般需要指定exception,
值 当返回相应的exception值时说明产生了异常,否则Cython对于特殊的cdef返回值函数
是不具备异常提醒机制的。
Cython 用于提升np.ndarray相关的性能的方法在于指定数据类型
cdef np.ndarray 及对ndarray下标访问时 如指定下标cdef int i
应该使用转型<unsigned int> i 代替i进行下标访问。
使用Cython 可以大大加快Python代码运行
这一般限于对于底层的一些C类型,而不是所有情况,例如对于一般的np.ndarray
这种已经使用C进行了较大优化的数组,这种情况是有限的,
而且对于诸如一些for循环, 当循环次数不够多时,Cython的加速也是捉襟见肘。
而且由于引入了新的类型及变量会导致运行时间更长,
有时候速度与内存是不可兼得的,
比如要使用一个Bagging算法(统计中的BootStrap)需要初始化抽样数组,
这时如果利用生成器对于每一个样本逐个生成,并进行后续处理,
会在占用较小内存的情况下完成任务,但速度不佳,
但也可以选择先生成所有抽样下标样本,这样会消耗较多的内存,但速度应当是更快的。
举一个例子,下面的代码是用来产生Bagging样本的pure python代码:
def generate_bagging_sample_index(X, sample_num, sample_size):
ListRequire = []
for i in range(sample_num):
ListRequire.append(np.random.randint(X.shape[1], size = sample_size))
return np.array(ListRequire)
def generate_bagging_sample_array(X, sample_num, sample_size):
sample_indexs_array = generate_bagging_sample_index(X, sample_num, sample_size)
for sample_indexs in sample_indexs_array:
yield X[sample_indexs].copy()
def Bagging_Test_Simple_Func(X, factor_max_bound = 20, sample_num = 10, sample_size = 100, disp = False, Type = "forward_stepwise", itertimes = None):
factor_dict = dict()
def change_eigen_ratio_sample_order(eigen_ratio_sample):
array_require_T = np.empty(shape = eigen_ratio_sample.T.shape)
for i in range(eigen_ratio_sample.T.shape[0]):
array_require_T[i] = list(reversed(list(eigen_ratio_sample.T[i])))
return (array_require_T.T).copy()
def generate_forward_stepwise_eigen_ratio_sample(X_bagging_sample):
eigen_ratio_sample = None
for i in range(factor_max_bound ,sample_size):
eigen_v = np.abs(list(reversed(list(eigvalsh(np.dot(X_bagging_sample[ :i + 1], X_bagging_sample[ :i + 1].T)))))) ** 0.5
if disp:
print (eigen_v[:-1] / eigen_v[1:])[: factor_max_bound]
print np.argmax((eigen_v[:-1] / eigen_v[1:])[: factor_max_bound])
if type(eigen_ratio_sample) == type(None):
eigen_ratio_sample = np.array((eigen_v[:-1] / eigen_v[1:])[: factor_max_bound]).reshape(1, -1)
else:
eigen_ratio_sample = np.append(eigen_ratio_sample, np.array((eigen_v[:-1] / eigen_v[1:])[: factor_max_bound]).reshape(1, -1), axis = 0)
return eigen_ratio_sample
这些函数的作用仅仅是生成样本及计算特征值的比,但当考虑性能采用Cython进行改进
会得到一个非常不具有可读性的版本:
def generate_bagging_sample_index(np.ndarray X, int sample_num, int sample_size):
cdef np.ndarray ListRequire = np.empty(shape = (sample_num, sample_size)), random_list
cdef int i, j
for i in range(sample_num):
random_list = np.random.randint(X.shape[1], size = sample_size)
for j in range(sample_size):
ListRequire[<unsigned int>i][<unsigned int>j] = random_list[<unsigned int>j]
return ListRequire
def generate_bagging_sample_array(np.ndarray X, int sample_num, int sample_size):
cdef np.ndarray sample_indexs_array = generate_bagging_sample_index(X, sample_num, sample_size)
cdef int i
for i in range(sample_indexs_array.shape[0]):
yield X[np.array(sample_indexs_array[<unsigned int>i], dtype = np.int)].copy()
def Bagging_Test_Simple_C_Func(np.ndarray X, int factor_max_bound = 20, int sample_num = 10, int sample_size = 100, disp = False, Type = "forward_stepwise", int itertimes = 0):
factor_dict = dict()
def change_eigen_ratio_sample_order(eigen_ratio_sample):
cdef np.ndarray array_require_T = np.empty(shape = eigen_ratio_sample.T.shape)
cdef int i
for i in range(eigen_ratio_sample.T.shape[0]):
array_require_T[<unsigned int>i] = list(reversed(list(eigen_ratio_sample.T[<unsigned int>i])))
return (array_require_T.T).copy()
def generate_forward_stepwise_eigen_ratio_sample(X_bagging_sample):
cdef np.ndarray eigen_ratio_sample = np.empty(shape = (sample_size - factor_max_bound, factor_max_bound)), ratio_list, eigen_v
cdef int i, j
for i in range(factor_max_bound ,sample_size):
eigen_v = np.abs(list(reversed(list(eigvalsh(np.dot(X_bagging_sample[ :<unsigned int>i + 1], X_bagging_sample[ :<unsigned int>i + 1].T)))))) ** 0.5
ratio_list = np.array((eigen_v[:-1] / eigen_v[1:])[: factor_max_bound])
for j in range(ratio_list.shape[0]):
eigen_ratio_sample[<unsigned int>i - factor_max_bound][<unsigned int>j] = ratio_list[<unsigned int>j]
return eigen_ratio_sample
在测试这两个程序时会发现,Cython比前者慢(不会慢太多),这与直接将前者进行Cython编译
的结果是近乎相同的,当数据量不够大时后者完败。
所以Bagging的性能消耗本质仍是内存与速度的取舍,而非编译语言。