beta
参数(形状为(4)的数组),我们可以生成各种纹理。以下是我使用 Numpy 的初始函数:
def gibbs_sampler(img_label, betas, burnin, nb_samples):
nb_iter = burnin + nb_samples
lst_samples = []
labels = np.unique(img)
M, N = img.shape
img_flat = img.flatten()
# build neighborhood array by means of numpy broadcasting:
m, n = np.ogrid[0:M, 0:N]
top_left, top, top_right = m[0:-2, :]*N + n[:, 0:-2], m[0:-2, :]*N + n[:, 1:-1] , m[0:-2, :]*N + n[:, 2:]
left, pix, right = m[1:-1, :]*N + n[:, 0:-2], m[1:-1, :]*N + n[:, 1:-1], m[1:-1, :]*N + n[:, 2:]
bottom_left, bottom, bottom_right = m[2:, :]*N + n[:, 0:-2], m[2:, :]*N + n[:, 1:-1], m[2:, :]*N + n[:, 2:]
mat_neigh = np.dstack([pix, top, bottom, left, right, top_left, bottom_right, bottom_left, top_right])
mat_neigh = mat_neigh.reshape((-1, 9))
ind = np.arange((M-2)*(N-2))
# loop over iterations
for iteration in np.arange(nb_iter):
np.random.shuffle(ind)
# loop over pixels
for i in ind:
truc = map(functools.partial(lambda label, img_flat, mat_neigh : 1-np.equal(label, img_flat[mat_neigh[i, 1:]]).astype(np.uint), img_flat=img_flat, mat_neigh=mat_neigh), labels)
# bidule is of shape (4, 2, labels.size)
bidule = np.array(truc).T.reshape((-1, 2, labels.size))
# theta is of shape (labels.size, 4)
theta = np.sum(bidule, axis=1).T
# prior is thus an array of shape (labels.size)
prior = np.exp(-np.dot(theta, betas))
# sample from the posterior
drawn_label = np.random.choice(labels, p=prior/np.sum(prior))
img_flat[(i//(N-2) + 1)*N + i%(N-2) + 1] = drawn_label
if iteration >= burnin:
print('Iteration %i --> sample' % iteration)
lst_samples.append(copy.copy(img_flat.reshape(M, N)))
else:
print('Iteration %i --> burnin' % iteration)
return lst_samples
由于循环是迭代算法的一部分,因此我们无法摆脱它。因此,我尝试使用Cython(带有静态类型)来加速它:
from __future__ import division
import numpy as np
import copy
cimport numpy as np
import functools
cimport cython
INTTYPE = np.int
DOUBLETYPE = np.double
ctypedef np.int_t INTTYPE_t
ctypedef np.double_t DOUBLETYPE_t
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def func_for_map(label, img_flat, mat_neigh, i):
return (1-np.equal(label, img_flat[mat_neigh[i, 1:]])).astype(INTTYPE)
def gibbs_sampler(np.ndarray[INTTYPE_t, ndim=2] img_label, np.ndarray[DOUBLETYPE_t, ndim=1] betas, INTTYPE_t burnin=5, INTTYPE_t nb_samples=1):
assert img_label.dtype == INTTYPE and betas.dtype== DOUBLETYPE
cdef unsigned int nb_iter = burnin + nb_samples
lst_samples = list()
cdef np.ndarray[INTTYPE_t, ndim=1] labels
labels = np.unique(img_label)
cdef unsigned int M, N
M = img_label.shape[0]
N = img_label.shape[1]
cdef np.ndarray[INTTYPE_t, ndim=1] ind
ind = np.arange((M-2)*(N-2), dtype=INTTYPE)
cdef np.ndarray[INTTYPE_t, ndim=1] img_flat
img_flat = img_label.flatten()
# build neighborhood array:
cdef np.ndarray[INTTYPE_t, ndim=2] m
cdef np.ndarray[INTTYPE_t, ndim=2] n
m = (np.ogrid[0:M, 0:N][0]).astype(INTTYPE)
n = (np.ogrid[0:M, 0:N][1]).astype(INTTYPE)
cdef np.ndarray[INTTYPE_t, ndim=2] top_left, top, top_right, left, pix, right, bottom_left, bottom, bottom_right
top_left, top, top_right = m[0:-2, :]*N + n[:, 0:-2], m[0:-2, :]*N + n[:, 1:-1] , m[0:-2, :]*N + n[:, 2:]
left, pix, right = m[1:-1, :]*N + n[:, 0:-2], m[1:-1, :]*N + n[:, 1:-1], m[1:-1, :]*N + n[:, 2:]
bottom_left, bottom, bottom_right = m[2:, :]*N + n[:, 0:-2], m[2:, :]*N + n[:, 1:-1], m[2:, :]*N + n[:, 2:]
cdef np.ndarray[INTTYPE_t, ndim=3] mat_neigh_init
mat_neigh_init = np.dstack([pix, top, bottom, left, right, top_left, bottom_right, bottom_left, top_right])
cdef np.ndarray[INTTYPE_t, ndim=2] mat_neigh
mat_neigh = mat_neigh_init.reshape((-1, 9))
cdef unsigned int i
truc = list()
cdef np.ndarray[INTTYPE_t, ndim=3] bidule
cdef np.ndarray[INTTYPE_t, ndim=2] theta
cdef np.ndarray[DOUBLETYPE_t, ndim=1] prior
cdef unsigned int drawn_label, iteration
# loop over ICE iterations
for iteration in np.arange(nb_iter):
np.random.shuffle(ind)
# loop over pixels
for i in ind:
truc = map(functools.partial(func_for_map, img_flat=img_flat, mat_neigh=mat_neigh, i=i), labels)
bidule = np.array(truc).T.reshape((-1, 2, labels.size)).astype(INTTYPE)
theta = np.sum(bidule, axis=1).T
# ok so far
prior = np.exp(-np.dot(theta, betas)).astype(DOUBLETYPE)
# print('ok after prior')
# return 0
# sample from the posterior
drawn_label = np.random.choice(labels, p=prior/np.sum(prior))
img_flat[(i//(N-2) + 1)*N + i%(N-2) + 1] = drawn_label
if iteration >= burnin:
print('Iteration %i --> sample' % iteration)
lst_samples.append(copy.copy(img_flat.reshape(M, N)))
else:
print('Iteration %i --> burnin' % iteration)
return lst_samples
然而,最终我得到了几乎相同的计算时间,其中numpy版本比Cython略快。因此,我正在尝试改进Cython的代码。
编辑:
对于两个函数(Cython和非Cython): 我已经替换了:
truc = map(functools.partial(lambda label, img_flat, mat_neigh : 1-np.equal(label, img_flat[mat_neigh[i, 1:]]).astype(np.uint), img_flat=img_flat, mat_neigh=mat_neigh), labels)
通过广播:
truc = 1-np.equal(labels[:, None], img_flat[mat_neigh[i, 1:]][None, :])
现在使用np.einsum
计算先验概率,range
已全部替换为np.arange
。这两个函数比以前都要快,但Python函数仍然比Cython函数略快。
cython
问题的评论,http://stackoverflow.com/questions/40233664/cython-actually-slowing-me-down。 - hpauljnp.exp
。 - floflo29labels
数组中使用二分查找来查找8个相邻像素,则可能将复杂度从O(n_iter * n_pixels * n_labels)降低到O(n_iter * n_pixels * log(n_labels))。也许吧。这需要一个棘手的自定义random.choice
方法,并且仅在n_labels很大时才值得使用... - user6758673