Why does hypothesis consider this code slow? - python

Hypothesis complains vehemently that this was slow:
#composite
def f_and_g_and_padding(draw, in_channels = channel_ints, out_channels = channel_ints, fs = shapes_2d, fill=None, elements=well_behaved_floats):
shape_f = draw(basic_shape)
padding = draw(shapes_2d)
fs = draw(fs)
in_channels = draw(in_channels)
out_channels = draw(out_channels)
batch_size = draw(shape_ints)
shape_f = (batch_size, in_channels, fs[0], fs[1])
f = draw(stnp.arrays(dt_numpy, shape_f, elements=elements, fill=fill))
h_in = f.shape[2] + padding[0] * 2
w_in = f.shape[3] + padding[1] * 2
shape_g = (out_channels, in_channels, h_in, w_in)
g = draw(stnp.arrays(dt_numpy, shape_g, elements=elements, fill=fill))
return (f, g, padding)
I have tried to find out why, but failed. See: How to use pytest, hypothesis and line_profiler / kernprof together?.
So, my question remains: Why?
Here are the other strategies used:
well_behaved_floats = stnp.from_dtype(dtype=dt_numpy, allow_infinity=False, allow_nan=False)
small_floats = stnp.from_dtype(dtype=dt_numpy, min_value=-10000, max_value=10000, allow_infinity=False, allow_nan=False)
floats_0_1 = stnp.from_dtype(dtype=dt_numpy, min_value=-1, max_value=1, allow_infinity=False, allow_nan=False)
small_ints = stnp.from_dtype(dtype=numpy.dtype("i4"), allow_infinity=False, allow_nan=False, min_value=-10, max_value=10)
small_positive_ints = stnp.from_dtype(dtype=numpy.dtype("i4"), allow_infinity=False, allow_nan=False, min_value=0, max_value=10)
one_or_greater = st.integers(min_value=1)
shape_ints = st.integers(min_value=1, max_value=4)
channel_ints = st.integers(min_value=1, max_value=10)
basic_shape = stnp.array_shapes(min_dims=4, max_dims=4, min_side=1, max_side=10)
ones = st.integers(min_value=1, max_value=1)
shapes_2d = stnp.array_shapes(min_dims=2, max_dims=2, min_side=1, max_side=4)
Used like this:
#given(f_and_g_and_padding(elements=ones))
def test_padding(f_g_padding: Tuple[numpy.ndarray, numpy.ndarray, Tuple[int, int]]):
f, g, padding = f_g_padding
run_test(Tensor(f), Tensor(g), padding=padding)
There's no filtering, just plain simple drawing and numpy arrays.
fwiw here's the hypothesis config:
hypothesis.settings.register_profile("default",
derandomize=True,
deadline=None,
print_blob=True,
report_multiple_bugs=False,
suppress_health_check=[HealthCheck.too_slow])

I'd expect that your basic_shapes strategy is the culprit; with a minimum of four dimensions you're already into n^4 elements in the average side length and that's going to be slow. Consider reducing the max_side for this strategy; if that's unacceptable you might need to generate shapes with Hypothesis but elements with numpy.random.
I'd also recommend against passing allow_infinity=False, allow_nan=False to strategies for integers, or for bounded floats - in either case non-finite numbers are already ruled out, so while they don't do anything it's a hit to readability.

Related

Understanding average (sum) pooling padding in keras

I have a simple sum pooling implemented in keras tensorflow, using AveragePooling2D*N*N, so it creates a sum of the elements in pool with some shape, same padding so the shape won't change:
import numpy as np
import seaborn as sns
import matplotlib.pylab as plt
import tensorflow as tf
from tensorflow.keras.backend import square
#generating the example matrix
def getMatrixByDefinitions(definitions, width, height):
matrix = np.zeros((width, height))
for definition in definitions:
x_cor = definition[1]
y_cor = definition[0]
value = definition[2]
matrix.itemset((x_cor, y_cor), value)
return matrix
generated = getMatrixByDefinitions(width=32, height=32, definitions =[[7,16,1]])
def avg_pool(pool):
return tf.keras.layers.AveragePooling2D(pool_size=(pool,pool), strides=(1, 1), padding='same')
def summer(pool, tensor):
return avg_pool(pool)(tensor)*pool*pool
def numpyToTensor(numpy_data):
numpy_as_array = np.asarray(numpy_data)
tensor_data = numpy_as_array.reshape(1, numpy_data.shape[1], numpy_data.shape[1], 1)
return tensor_data
data = numpyToTensor(generated)
pooled_data = summer(11, data)
def printMatrixesToHeatMap(matrixes, title):
# f = pyplot.figure() # width and height in inches
matrix_count = len(matrixes)
width_ratios = [4] * matrix_count + [0.2]
mergedMatrixes = matrixes[0][0]
for matrix in matrixes:
mergedMatrixes = np.concatenate((mergedMatrixes, matrix[0]), axis=0)
vmin = np.min(mergedMatrixes)
vmax = np.max(mergedMatrixes)
fig, axs = plt.subplots(ncols=matrix_count + 1, gridspec_kw=dict(width_ratios=width_ratios))
fig.set_figheight(20)
fig.set_figwidth(20 * matrix_count + 5)
axis_id = 0
for matrix in matrixes:
sns.heatmap(matrix[0], annot=True, cbar=False, ax=axs[axis_id], vmin=vmin, vmax=vmax)
axs[axis_id].set_title(matrix[1])
axis_id = axis_id + 1
#fig.colorbar(axs[1].collections[0], cax=axs[matrix_count])
fig.savefig(title+".pdf", bbox_inches='tight')
def tensorToNumpy(tensor):
width = tensor.get_shape()[1]
height = tensor.get_shape()[2]
output = tf.reshape(tensor, [width, height])
#output = output.eval(session=tf.compat.v1.Session())
output = output.numpy()
return np.array(output)
printMatrixesToHeatMap([[tensorToNumpy(pooled_data), "Pooled data"]],
"name")
After testing it on very simple 2D array I have found out it does not do what I expect (original and pooled data):
You can see that the single one sum-pooled (according to average pooling) ended up with sum greater than real sum, which is 1, near the borders. (in this case max can be used, but the real data are more complex and we need sum) This would mean that average near borders is count not from padded data but the original. Or is this misunderstanding of padding from my side? I need to have ones on indices where 1.1, 1.2, 1.4 is. Why is this and how can I solve such problem?
Note that I do not want to manually set the correct sum, so I am looking for a way to achieve this in keras pooling itself.
It seems to be a problem with the "SAME" padding algorithm. Unfortunately,there is no way of specifying an explicit padding to the avg_pool2d op. It is possible to manually pad the input with tf.pad though. Here is a really naive approach to padding that will work with odd shaped pooling filters and strides size of 1 :
generated = getMatrixByDefinitions(width=32, height=32, definitions =[[7,16,1]])
gen_nhwc = tf.constant(generated[np.newaxis,:,:,np.newaxis])
pool = 11
paddings = [[0,0],[pool//2,pool//2],[pool//2,pool//2],[0,0]]
gen_pad = tf.pad(gen_nhwc, paddings, "CONSTANT")
res = tf.nn.avg_pool2d(gen_pad, (pool,pool), (1,1),"VALID")*pool*pool
result = np.squeeze(res.numpy())
printMatrixesToHeatMap([[generated, "input"],[result, "output"]], "name")
Results in images :
Edit : I created an issue on Github regarding the problem.

Is it possible to convert this numpy function to tensorflow?

I have a function that takes a [32, 32, 3] tensor, and outputs a [256,256,3] tensor.
Specifically, the function interprets the smaller array as if it was a .svg file, and 'renders' it to a 256x256 array as a canvas using this algorithm
For an explanation of WHY I would want to do this, see This question
The function behaves exactly as intended, until I try to include it in the training loop of a GAN. The current error I'm seeing is:
NotImplementedError: Cannot convert a symbolic Tensor (mul:0) to a numpy array.
A lot of other answers to similar errors seem to boil down to "You need to re-write the function using tensorflow, not numpy"
Here's the working code using numpy - is it possible to re-write it to exclusively use tensorflow functions?
def convert_to_bitmap(input_tensor, target, j):
#implied conversion to nparray - the tensorflow docs seem to indicate this is okay, but the error is thrown here when training
array = input_tensor
outputArray = target
output = target
for i in range(32):
col = float(array[i,0,j])
if ((float(array[i,0,0]))+(float(array[i,0,1]))+(float(array[i,0,2]))/3)< 0:
continue
#slice only the red channel from the i line, multiply by 255
red_array = array[i,:,0]*255
#slice only the green channel, multiply by 255
green_array = array[i,:,1]*255
#combine and flatten them
combined_array = np.dstack((red_array, green_array)).flatten()
#remove the first two and last two indices of the combined array
index = [0,1,62,63]
clipped_array = np.delete(combined_array,index)
#filter array to remove values less than 0
filtered = clipped_array > 0
filtered_array = clipped_array[filtered]
#check array has an even number of values, delete the last index if it doesn't
if len(filtered_array) % 2 == 0:
pass
else:
filtered_array = np.delete(filtered_array,-1)
#convert into a set of tuples
l = filtered_array.tolist()
t = list(zip(l, l[1:] + l[:1]))
if not t:
continue
output = fill_polygon(t, outputArray, col)
return(output)
The 'fill polygon' function is copied from the 'mahotas' library:
def fill_polygon(polygon, canvas, color):
if not len(polygon):
return
min_y = min(int(y) for y,x in polygon)
max_y = max(int(y) for y,x in polygon)
polygon = [(float(y),float(x)) for y,x in polygon]
if max_y < canvas.shape[0]:
max_y += 1
for y in range(min_y, max_y):
nodes = []
j = -1
for i,p in enumerate(polygon):
pj = polygon[j]
if p[0] < y and pj[0] >= y or pj[0] < y and p[0] >= y:
dy = pj[0] - p[0]
if dy:
nodes.append( (p[1] + (y-p[0])/(pj[0]-p[0])*(pj[1]-p[1])) )
elif p[0] == y:
nodes.append(p[1])
j = i
nodes.sort()
for n,nn in zip(nodes[::2],nodes[1::2]):
nn += 1
canvas[y, int(n):int(nn)] = color
return(canvas)
NOTE: I'm not trying to get someone to convert the whole thing for me! There are some functions that are pretty obvious (tf.stack instead of np.dstack), but others that I don't even know how to start, like the last few lines of the fill_polygon function above.
Yes you can actually do this, you can use a python function in sth called tf.pyfunc. Its a python wrapper but its extremely slow in comparison to plain tensorflow. However, tensorflow and Cuda for example are so damn fast because they use stuff like vectorization, meaning you can rewrite a lot , really many of the loops in terms of mathematical tensor operations which are very fast.
In general:
If you want to use custom code as a custom layer, i would recommend you to rethink the algebra behind those loops and try to express them somehow different. If its just preprocessing before the training is going to start, you can use tensorflow but doing the same with numpy and other libraries is easier.
To your main question: Yes its possible, but better dont use loops. Tensorflow has a build-in loop optimizer but then you have to use tf.while() and thats anyoing (maybe just for me). I just blinked over your code, but it looks like you should be able to vectorize it quite good using the standard tensorflow vocabulary. If you want it fast, i mean really fast with GPU support write all in tensorflow, but nothing like 50/50 with tf.convert_to_tensor(), because than its going to be slow again. because than you switch between GPU and CPU and plain Python interpreter and the tensorflow low level API. Hope i could help you at least a bit
This code 'works', in that it only uses tensorflow functions, and does allow the model to train when used in a training loop:
def convert_image (x):
#split off the first column of the generator output, and store it for later (remove the 'colours' column)
colours_column = tf.slice(img_to_convert, tf.constant([0,0,0], dtype=tf.int32), tf.constant([32,1,3], dtype=tf.int32))
#split off the rest of the data, only keeping R + G, and discarding B
image_data_red = tf.slice(img_to_convert, tf.constant([0,1,0], dtype=tf.int32), tf.constant([32,31,1], dtype=tf.int32))
image_data_green = tf.slice(img_to_convert, tf.constant([0,1,1], dtype=tf.int32), tf.constant([32, 31,1], dtype=tf.int32))
#roll each row by 1 position, and make two more 2D tensors
rolled_red = tf.roll(image_data_red, shift=-1, axis=0)
rolled_green = tf.roll(image_data_green, shift=-1, axis=0)
#remove all values where either the red OR green channels are 0
zeroes = tf.constant(0, dtype=tf.float32)
#this is for the 'count_nonzero' command
boolean_red_data = tf.not_equal(image_data_red, zeroes)
boolean_green_data = tf.not_equal(image_data_green, zeroes)
initial_data_mask = tf.logical_and(boolean_red_data, boolean_green_data)
#count non-zero values per row and flatten it
count = tf.math.count_nonzero(initial_data_mask, 1)
count_flat = tf.reshape(count, [-1])
flat_red = tf.reshape(image_data_red, [-1])
flat_green = tf.reshape(image_data_green, [-1])
boolean_red = tf.math.logical_not(tf.equal(flat_red, tf.zeros_like(flat_red)))
boolean_green = tf.math.logical_not(tf.equal(flat_green, tf.zeros_like(flat_red)))
mask = tf.logical_and(boolean_red, boolean_green)
flat_red_without_zero = tf.boolean_mask(flat_red, mask)
flat_green_without_zero = tf.boolean_mask(flat_green, mask)
# create a ragged tensor
X0_ragged = tf.RaggedTensor.from_row_lengths(values=flat_red_without_zero, row_lengths=count_flat)
Y0_ragged = tf.RaggedTensor.from_row_lengths(values=flat_green_without_zero, row_lengths=count_flat)
#do the same for the rolled version
rolled_data_mask = tf.roll(initial_data_mask, shift=-1, axis=1)
flat_rolled_red = tf.reshape(rolled_red, [-1])
flat_rolled_green = tf.reshape(rolled_green, [-1])
#from SO "shift zeros to the end"
boolean_rolled_red = tf.math.logical_not(tf.equal(flat_rolled_red, tf.zeros_like(flat_rolled_red)))
boolean_rolled_green = tf.math.logical_not(tf.equal(flat_rolled_green, tf.zeros_like(flat_rolled_red)))
rolled_mask = tf.logical_and(boolean_rolled_red, boolean_rolled_green)
flat_rolled_red_without_zero = tf.boolean_mask(flat_rolled_red, rolled_mask)
flat_rolled_green_without_zero = tf.boolean_mask(flat_rolled_green, rolled_mask)
# create a ragged tensor
X1_ragged = tf.RaggedTensor.from_row_lengths(values=flat_rolled_red_without_zero, row_lengths=count_flat)
Y1_ragged = tf.RaggedTensor.from_row_lengths(values=flat_rolled_green_without_zero, row_lengths=count_flat)
#available outputs for future use are:
X0 = X0_ragged.to_tensor(default_value=0.)
Y0 = Y0_ragged.to_tensor(default_value=0.)
X1 = X1_ragged.to_tensor(default_value=0.)
Y1 = Y1_ragged.to_tensor(default_value=0.)
#Example tensor cel (replace with (x))
P = tf.cast(x, dtype=tf.float32)
#split out P.x and P.y, and fill a ragged tensor to the same shape as Rx
Px_value = tf.cast(x, dtype=tf.float32) - tf.cast((tf.math.floor(x/255)*255), dtype=tf.float32)
Py_value = tf.cast(tf.math.floor(x/255), dtype=tf.float32)
Px = tf.squeeze(tf.ones_like(X0)*Px_value)
Py = tf.squeeze(tf.ones_like(Y0)*Py_value)
#for each pair of values (Y0, Y1, make a vector, and check to see if it crosses the y-value (Py) either up or down
a = tf.math.less(Y0, Py)
b = tf.math.greater_equal(Y1, Py)
c = tf.logical_and(a, b)
d = tf.math.greater_equal(Y0, Py)
e = tf.math.less(Y1, Py)
f = tf.logical_and(d, e)
g = tf.logical_or(c, f)
#Makes boolean bitwise mask
#calculate the intersection of the line with the y-value, assuming it intersects
#P.x <= (G.x - R.x) * (P.y - R.y) / (G.y - R.y + R.x) - use tf.divide_no_nan for safe divide
h = tf.math.less(Px,(tf.math.divide_no_nan(((X1-X0)*(Py-Y0)),(Y1-Y0+X0))))
#combine using AND with the mask above
i = tf.logical_and(g,h)
#tf.count_nonzero
#reshape to make a column tensor with the same dimensions as the colours
#divide by 2 using tf.floor_mod (returns remainder of division - any remainder means the value is odd, and hence the point is IN the polygon)
final_count = tf.cast((tf.math.count_nonzero(i, 1)), dtype=tf.int32)
twos = tf.ones_like(final_count, dtype=tf.int32)*tf.constant([2], dtype=tf.int32)
divide = tf.cast(tf.math.floormod(final_count, twos), dtype=tf.int32)
index = tf.cast(tf.range(0,32, delta=1), dtype=tf.int32)
clipped_index = divide*index
sort = tf.sort(clipped_index)
reverse = tf.reverse(sort, [-1])
value = tf.slice(reverse, [0], [1])
pair = tf.constant([0], dtype=tf.int32)
slice_tensor = tf.reshape(tf.stack([value, pair, pair], axis=0),[-1])
output_colour = tf.slice(colours_column, slice_tensor, [1,1,3])
return output_colour
This is where the 'convert image' function is applied using tf.vectorize_map:
def convert_images(image_to_convert):
global img_to_convert
img_to_convert = image_to_convert
process_list = tf.reshape((tf.range(0,65536, delta=1, dtype=tf.int32)), [65536, 1])
output_line = tf.vectorized_map(convert_image, process_list)
output_line_squeezed = tf.squeeze(output_line)
output_reshape = (tf.reshape(output_line_squeezed, [256,256,3])/127.5)-1
output = tf.expand_dims(output_reshape, axis=0)
return output
It is PAINFULLY slow, though - It does not appear to be using the GPU, and looks to be single threaded as well.
I'm adding it as an answer to my own question because is clearly IS possible to do this numpy function entirely in tensorflow - it just probably shouldn't be done like this.

Modifying the cost function in TensorFlow

I want to modify the following cost function in a way that it adds extra weight to the samples where the prediction is higher than the true output!
cost = tf.reduce_sum(tf.pow(logits-Y, 2))/(2*batch_size)
I found it to be tricky in Tensorflow operations! I want to use Tensorflow operations to do the following codes (written by numpy):
batch_szie = 100
label = np.random.normal(size=batch_szie)
cost = (np.sum(np.power((2*label [label >=0]),2)) + np.sum(np.power((2*label [label <0]),2)))/batch_szie
Please note that the first two lines are just for simulating the label = logits-Y.
Any help/suggestion? Thanks :)
Here I found an answer to this question. However, I think there should be easier and more concise ways.
batch_size = 4
labels = tf.constant ([1,-1,2,1])
pos_index = tf.where(tf.greater_equal(labels, 0))
pos_index = tf.reshape(pos_index, [-1])
pos_label = 5 * tf.gather(labels, pos_index)
neg_index = tf.where(tf.less_equal(labels, 0))
neg_index = tf.reshape(neg_index, [-1])
neg_label = tf.gather(labels, neg_index)
cost = (tf.reduce_sum(tf.pow(pos_label, 2)) + tf.reduce_sum(tf.pow(neg_label, 2)))/(2*batch_size)
with tf.Session() as sess:
print(sess.run(cost))

Theano reshape – index out ouf bounds

I can't seem to get Theano to reshape my tensors as want it to. The reshaping in the code bellow is supposed to keep keep_dims dimensions and flatten all remaining ones into a single array.
The code fails with IndexError: index out of bounds on the reshape line if I run it with a test value. Otherwise, the function seems to compile, but fails upon first real input with ValueError: total size of new array must be unchanged.
When I tried using just numpy for an equivalent code, it worked normally. Is there anything I am doing wrong? Or is there any easy way to see the resulting dimensions that are used for the reshaping (ipdb does not help since everything is a Theano variable)?
import theano
import theano.tensor as T
import numpy as np
theano.config.compute_test_value = 'warn'
theano.config.optimizer = 'None'
class Layer(object):
def __init__(self, name):
self.name = name
self.inputs = []
self.outputs = []
def get_init_weights(self, shape):
rows, cols = shape
w_init = np.reshape(np.asarray([rnd.uniform(-0.05, 0.05)
for _ in xrange(rows * cols)]),
newshape=(rows, cols))
return w_init
class Embedding(Layer):
def __init__(self, name, dict_size, width, init='uniform_005'):
super(Embedding, self).__init__(name)
self.width = width
self.dict_size = dict_size
e_init = self.get_init_weights((dict_size, width))
self.e = theano.shared(value=e_init, name=self.name)
def connect(self, inputs):
output = self.e[inputs]
self.inputs.append(inputs)
self.outputs.append(output)
return output
class Flatten(Layer):
def __init__(self, name, keep_dims=1):
super(Flatten, self).__init__(name)
self.params = []
self.keep_dims = keep_dims
def connect(self, inputs):
keep_dims = self.keep_dims
# this line fails
output = inputs.reshape(inputs.shape[0:keep_dims] +
(T.prod(inputs.shape[keep_dims:]),),
ndim=(keep_dims + 1))
return output
if __name__ == '__main__':
x = T.itensor3('x') # batch embedding * embedding size * number of different embeddings
x.tag.test_value = np.random.randint(0, 50, (5, 20, 3)).astype('int32')
emb_layer = Embedding('e', dict_size=50, width=10)
y = emb_layer.connect(x)
flat_layer = Flatten('f')
y = flat_layer.connect(y)
func = theano.function([x], y, allow_input_downcast=True)
The problem relates to how you're combining the two components of the new shape. The reshape command requires an lvector for the new shape.
Since you're using the test values mechanism you can debug this problem by simply printing test value bits and pieces. For example, I used
print inputs.shape.tag.test_value
print inputs.shape[0:keep_dims].tag.test_value
print inputs.shape[keep_dims:].tag.test_value
print T.prod(inputs.shape[keep_dims:]).tag.test_value
print (inputs.shape[0:keep_dims] + (T.prod(inputs.shape[keep_dims:]),)).tag.test_value
print T.concatenate([inputs.shape[0:keep_dims], [T.prod(inputs.shape[keep_dims:])]]).tag.test_value
This shows a fix to the problem: using T.concatenate to combine the keep_dims and the product of the remaining dims.

How to run a .py module?

I've got zero experience with Python. I have looked around some tutorial materials, but it seems difficult to understand a advanced code. So I came here for a more specific answer.
For me the mission is to redo the code in my computer.
Here is the scenario:
I'm a graduate student studying tensor factorization in relation learning. A paper[1] providing a code to run this algorithm, as follows:
import logging, time
from numpy import dot, zeros, kron, array, eye, argmax
from numpy.linalg import qr, pinv, norm, inv
from scipy.linalg import eigh
from numpy.random import rand
__version__ = "0.1"
__all__ = ['rescal', 'rescal_with_random_restarts']
__DEF_MAXITER = 500
__DEF_INIT = 'nvecs'
__DEF_PROJ = True
__DEF_CONV = 1e-5
__DEF_LMBDA = 0
_log = logging.getLogger('RESCAL')
def rescal_with_random_restarts(X, rank, restarts=10, **kwargs):
"""
Restarts RESCAL multiple time from random starting point and
returns factorization with best fit.
"""
models = []
fits = []
for i in range(restarts):
res = rescal(X, rank, init='random', **kwargs)
models.append(res)
fits.append(res[2])
return models[argmax(fits)]
def rescal(X, rank, **kwargs):
"""
RESCAL
Factors a three-way tensor X such that each frontal slice
X_k = A * R_k * A.T. The frontal slices of a tensor are
N x N matrices that correspond to the adjecency matrices
of the relational graph for a particular relation.
For a full description of the algorithm see:
Maximilian Nickel, Volker Tresp, Hans-Peter-Kriegel,
"A Three-Way Model for Collective Learning on Multi-Relational Data",
ICML 2011, Bellevue, WA, USA
Parameters
----------
X : list
List of frontal slices X_k of the tensor X. The shape of each X_k is ('N', 'N')
rank : int
Rank of the factorization
lmbda : float, optional
Regularization parameter for A and R_k factor matrices. 0 by default
init : string, optional
Initialization method of the factor matrices. 'nvecs' (default)
initializes A based on the eigenvectors of X. 'random' initializes
the factor matrices randomly.
proj : boolean, optional
Whether or not to use the QR decomposition when computing R_k.
True by default
maxIter : int, optional
Maximium number of iterations of the ALS algorithm. 500 by default.
conv : float, optional
Stop when residual of factorization is less than conv. 1e-5 by default
Returns
-------
A : ndarray
array of shape ('N', 'rank') corresponding to the factor matrix A
R : list
list of 'M' arrays of shape ('rank', 'rank') corresponding to the factor matrices R_k
f : float
function value of the factorization
iter : int
number of iterations until convergence
exectimes : ndarray
execution times to compute the updates in each iteration
"""
# init options
ainit = kwargs.pop('init', __DEF_INIT)
proj = kwargs.pop('proj', __DEF_PROJ)
maxIter = kwargs.pop('maxIter', __DEF_MAXITER)
conv = kwargs.pop('conv', __DEF_CONV)
lmbda = kwargs.pop('lmbda', __DEF_LMBDA)
if not len(kwargs) == 0:
raise ValueError( 'Unknown keywords (%s)' % (kwargs.keys()) )
sz = X[0].shape
dtype = X[0].dtype
n = sz[0]
k = len(X)
_log.debug('[Config] rank: %d | maxIter: %d | conv: %7.1e | lmbda: %7.1e' % (rank,
maxIter, conv, lmbda))
_log.debug('[Config] dtype: %s' % dtype)
# precompute norms of X
normX = [norm(M)**2 for M in X]
Xflat = [M.flatten() for M in X]
sumNormX = sum(normX)
# initialize A
if ainit == 'random':
A = array(rand(n, rank), dtype=dtype)
elif ainit == 'nvecs':
S = zeros((n, n), dtype=dtype)
T = zeros((n, n), dtype=dtype)
for i in range(k):
T = X[i]
S = S + T + T.T
evals, A = eigh(S,eigvals=(n-rank,n-1))
else :
raise 'Unknown init option ("%s")' % ainit
# initialize R
if proj:
Q, A2 = qr(A)
X2 = __projectSlices(X, Q)
R = __updateR(X2, A2, lmbda)
else :
R = __updateR(X, A, lmbda)
# compute factorization
fit = fitchange = fitold = f = 0
exectimes = []
ARAt = zeros((n,n), dtype=dtype)
for iter in xrange(maxIter):
tic = time.clock()
fitold = fit
A = __updateA(X, A, R, lmbda)
if proj:
Q, A2 = qr(A)
X2 = __projectSlices(X, Q)
R = __updateR(X2, A2, lmbda)
else :
R = __updateR(X, A, lmbda)
# compute fit value
f = lmbda*(norm(A)**2)
for i in range(k):
ARAt = dot(A, dot(R[i], A.T))
f += normX[i] + norm(ARAt)**2 - 2*dot(Xflat[i], ARAt.flatten()) + lmbda*(R[i].flatten()**2).sum()
f *= 0.5
fit = 1 - f / sumNormX
fitchange = abs(fitold - fit)
toc = time.clock()
exectimes.append( toc - tic )
_log.debug('[%3d] fit: %.5f | delta: %7.1e | secs: %.5f' % (iter,
fit, fitchange, exectimes[-1]))
if iter > 1 and fitchange < conv:
break
return A, R, f, iter+1, array(exectimes)
def __updateA(X, A, R, lmbda):
n, rank = A.shape
F = zeros((n, rank), dtype=X[0].dtype)
E = zeros((rank, rank), dtype=X[0].dtype)
AtA = dot(A.T,A)
for i in range(len(X)):
F += dot(X[i], dot(A, R[i].T)) + dot(X[i].T, dot(A, R[i]))
E += dot(R[i], dot(AtA, R[i].T)) + dot(R[i].T, dot(AtA, R[i]))
A = dot(F, inv(lmbda * eye(rank) + E))
return A
def __updateR(X, A, lmbda):
r = A.shape[1]
R = []
At = A.T
if lmbda == 0:
ainv = dot(pinv(dot(At, A)), At)
for i in range(len(X)):
R.append( dot(ainv, dot(X[i], ainv.T)) )
else :
AtA = dot(At, A)
tmp = inv(kron(AtA, AtA) + lmbda * eye(r**2))
for i in range(len(X)):
AtXA = dot(At, dot(X[i], A))
R.append( dot(AtXA.flatten(), tmp).reshape(r, r) )
return R
def __projectSlices(X, Q):
q = Q.shape[1]
X2 = []
for i in range(len(X)):
X2.append( dot(Q.T, dot(X[i], Q)) )
return X2
It's boring to paste such a long code but there is no other way to figure out my problems. I'm sorry about this.
I import this module and pass them arguments according to the author's website:
import pickle, sys
from rescal import rescal
rank = sys.argv[1]
X = pickle.load('us-presidents.pickle')
A, R, f, iter, exectimes = rescal(X, rank, lmbda=1.0)
The dataset us-presidents.rdf can be found here.
My questions are:
According to the code note, the tensor X is a list. I don't quite understand this, how do I relate a list to a tensor in Python? Can I understand tensor = list in Python?
Should I convert RDF format to a triple(subject, predicate, object) format first? I'm not sure of the data structure of X. How do I assignment values to X by hand?
Then, how to run it?
I paste the author's code without his authorization, is it an act of infringement? if so, I am so sorry and I will delete it soon.
The problems may be a little bored, but these are important to me. Any help would be greatly appreciated.
[1] Maximilian Nickel, Volker Tresp, Hans-Peter Kriegel,
A Three-Way Model for Collective Learning on Multi-Relational Data,
in Proceedings of the 28th International Conference on Machine Learning, 2011 , Bellevue, WA, USA
To answer Q2: you need to transform the RDF and save it before you can load it from the file 'us-presidents.pickle'. The author of that code probably did that once because the Python native pickle format loads faster. As the pickle format includes the datatype of the data, it is possible that X is some numpy class instance and you would need either an example pickle file as used by this code, or some code doing the pickle.dump to figure out how to convert from RDF to this particular pickle file as rescal expects it.
So this might answer Q1: the tensor consists of a list of elements. From the code you can see that the X parameter to rescal has a length (k = len(X) ) and can be indexed (T = X[i]). So it elements are used as a list (even if it might be some other datatype, that just behaves as such.
As an aside: If you are not familiar with Python and are just interested in the result of the computation, you might get more help contacting the author of the software.
According to the code note, the tensor X is a list. I don't quite understand this, how do I relate a list to a tensor in Python? Can I
understand tensor = list in Python?
Not necessarily but the author of the code has decided to represent the tensor data as a list data structure. As the comments indicate, the list X contains:
List of frontal slices X_k of the tensor X. The shape of each X_k is ('N', 'N')
That means the tensor is repesented as a list of tuples: [(N, N), ..., (N, N)].
I'm not sure of the data structure of X. How do I assignment values to X by hand?
Now that we now the data structure of X, we can assign values to it using assignment. The following will assign the tuple (1, 3) to the first position in the list X (as the first position is at index 0, the second at position 1, et cetera):
X[0] = (1, 3)
Similarly, the following will assign the tuple (2, 4) to the second position:
X[1] = (2, 4)

Categories

Resources