Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
from redisai import Client, Tensor, \
BlobTensor, DType, Device, Backend
from redisai import Client, DType, Device, Backend
import ml2rt

client = Client()
client.tensorset('x', Tensor(DType.float, [2], [2, 3]))
client.tensorset('x', [2, 3], dtype=DType.float)
t = client.tensorget('x')
print(t.value)

model = ml2rt.load_model('test/testdata/graph.pb')
client.tensorset('a', Tensor.scalar(DType.float, 2, 3))
client.tensorset('b', Tensor.scalar(DType.float, 12, 10))
client.tensorset('a', (2, 3), dtype=DType.float)
client.tensorset('b', (12, 10), dtype=DType.float)
client.modelset('m', Backend.tf,
Device.cpu,
input=['a', 'b'],
output='mul',
inputs=['a', 'b'],
outputs='mul',
data=model)
client.modelrun('m', ['a', 'b'], ['mul'])
print(client.tensorget('mul').value)

# Try with a script
script = ml2rt.load_script('test/testdata/script.txt')
client.scriptset('ket', Device.cpu, script)
client.scriptrun('ket', 'bar', input=['a', 'b'], output='c')
client.scriptrun('ket', 'bar', inputs=['a', 'b'], outputs='c')

b1 = client.tensorget('c', as_type=BlobTensor)
b2 = client.tensorget('c', as_type=BlobTensor)

client.tensorset('d', BlobTensor(DType.float, b1.shape, b1, b2))

tnp = b1.to_numpy()
client.tensorset('e', tnp)
1 change: 0 additions & 1 deletion redisai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from .version import __version__
from .client import Client
from .tensor import Tensor, BlobTensor
from .constants import DType, Device, Backend
71 changes: 32 additions & 39 deletions redisai/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from redis import StrictRedis
from typing import Union, Any, AnyStr, ByteString, Sequence, Type
from typing import Union, Any, AnyStr, ByteString, Sequence
from .containers import Script, Model, Tensor

try:
import numpy as np
Expand All @@ -8,7 +9,7 @@

from .constants import Backend, Device, DType
from .utils import str_or_strsequence, to_string
from .tensor import Tensor, BlobTensor
from . import tensorize


class Client(StrictRedis):
Expand Down Expand Up @@ -45,13 +46,12 @@ def modelset(self,
args += [data]
return self.execute_command(*args)

def modelget(self, name: AnyStr) -> dict:
def modelget(self, name: AnyStr) -> Model:
rv = self.execute_command('AI.MODELGET', name)
return {
'backend': Backend(to_string(rv[0])),
'device': Device(to_string(rv[1])),
'data': rv[2]
}
return Model(
rv[2],
Device(to_string(rv[1])),
Backend(to_string(rv[0])))

def modeldel(self, name: AnyStr) -> AnyStr:
return self.execute_command('AI.MODELDEL', name)
Expand All @@ -68,71 +68,64 @@ def modelrun(self,

def tensorset(self,
key: AnyStr,
tensor: Union[Tensor, np.ndarray, list, tuple],
tensor: Union[np.ndarray, list, tuple],
shape: Union[Sequence[int], None] = None,
dtype: Union[DType, None] = None) -> Any:
"""
Set the values of the tensor on the server using the provided Tensor object
:param key: The name of the tensor
:param tensor: a `Tensor` object
:param shape: Shape of the tensor
:param dtype: data type of the tensor. Required if input is a sequence of ints/floats
:param tensor: a `np.ndarray` object or python list or tuple
:param shape: Shape of the tensor. Required if `tensor` is list or tuple
:param dtype: data type of the tensor. Required if `tensor` is list or tuple
"""
# TODO: tensorset will not accept BlobTensor or Tensor object in the future.
# Keeping it in the current version for compatibility with the example repo
if np and isinstance(tensor, np.ndarray):
tensor = BlobTensor.from_numpy(tensor)
tensor = tensorize.from_numpy(tensor)
args = ['AI.TENSORSET', key, tensor.dtype.value, *tensor.shape, tensor.argname, tensor.value]
elif isinstance(tensor, (list, tuple)):
if shape is None:
shape = (len(tensor),)
tensor = Tensor(dtype, shape, tensor)
args = ['AI.TENSORSET', key, tensor.type.value]
args += tensor.shape
args += [tensor.ARGNAME]
args += tensor.value
tensor = tensorize.from_sequence(tensor, shape, dtype)
args = ['AI.TENSORSET', key, tensor.dtype.value, *tensor.shape, tensor.argname, *tensor.value]
return self.execute_command(*args)

def tensorget(self,
key: AnyStr, as_type: Type[Tensor] = None,
meta_only: bool = False) -> Union[Tensor, BlobTensor]:
key: AnyStr, as_numpy: bool = True,
meta_only: bool = False) -> Union[Tensor, np.ndarray]:
"""
Retrieve the value of a tensor from the server. By default it returns the numpy array
but it can be controlled using `as_type` argument and `meta_only` argument.
:param key: the name of the tensor
:param as_type: the resultant tensor type. Returns numpy array if None
:param as_numpy: Should it return data as numpy.ndarray.
Wraps with namedtuple if False. This flag also decides how to fetch the
value from RedisAI server and could have performance implications
:param meta_only: if true, then the value is not retrieved,
only the shape and the type
:return: an instance of as_type
"""
# TODO; We might remove Tensor & BlobTensor in the future and `tensorget` will return
# python list or numpy arrays or a namedtuple
if meta_only:
argname = 'META'
elif as_type is None:
argname = BlobTensor.ARGNAME
elif as_numpy is True:
argname = 'BLOB'
else:
argname = as_type.ARGNAME
argname = 'VALUES'

res = self.execute_command('AI.TENSORGET', key, argname)
dtype, shape = to_string(res[0]), res[1]
dt = DType.__members__[dtype.lower()]
if meta_only:
return Tensor(dt, shape, [])
elif as_type is None:
return BlobTensor.from_resp(dt, shape, res[2]).to_numpy()
return tensorize.to_sequence([], shape, dtype)
if as_numpy is True:
return tensorize.to_numpy(res[2], shape, dtype)
else:
return as_type.from_resp(dt, shape, res[2])
return tensorize.to_sequence(res[2], shape, dtype)

def scriptset(self, name: AnyStr, device: Device, script: AnyStr) -> AnyStr:
return self.execute_command('AI.SCRIPTSET', name, device.value, script)

def scriptget(self, name: AnyStr) -> dict:
def scriptget(self, name: AnyStr) -> Script:
r = self.execute_command('AI.SCRIPTGET', name)
device = Device(to_string(r[0]))
return {
'device': device,
'script': to_string(r[1])
}
return Script(
to_string(r[1]),
Device(to_string(r[0])))

def scriptdel(self, name):
return self.execute_command('AI.SCRIPTDEL', name)
Expand Down
1 change: 1 addition & 0 deletions redisai/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Backend(Enum):
tf = 'TF'
torch = 'TORCH'
onnx = 'ONNX'
tflite = 'TFLITE'


class DType(Enum):
Expand Down
5 changes: 5 additions & 0 deletions redisai/containers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from collections import namedtuple

Tensor = namedtuple('Tensor', field_names=['value', 'shape', 'dtype', 'argname'])
Script = namedtuple('Script', field_names=['script', 'device'])
Model = namedtuple('Model', field_names=['data', 'device', 'backend'])
120 changes: 0 additions & 120 deletions redisai/tensor.py

This file was deleted.

43 changes: 43 additions & 0 deletions redisai/tensorize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Union, ByteString, Sequence
from .utils import convert_to_num
from .constants import DType
from .containers import Tensor
try:
import numpy as np
except (ImportError, ModuleNotFoundError):
np = None


def from_numpy(tensor: np.ndarray) -> Tensor:
""" Convert the numpy input from user to `Tensor` """
dtype = DType.__members__[str(tensor.dtype)]
shape = tensor.shape
blob = bytes(tensor.data)
return Tensor(blob, shape, dtype, 'BLOB')


def from_sequence(tensor: Sequence, shape: Union[list, tuple], dtype: DType) -> Tensor:
""" Convert the `list`/`tuple` input from user to `Tensor` """
return Tensor(tensor, shape, dtype, 'VALUES')


def to_numpy(value: ByteString, shape: Union[list, tuple], dtype: DType) -> np.ndarray:
""" Convert `BLOB` result from RedisAI to `np.ndarray` """
dtype = DType.__members__[dtype.lower()].value
mm = {
'FLOAT': 'float32',
'DOUBLE': 'float64'
}
if dtype in mm:
dtype = mm[dtype]
else:
dtype = dtype.lower()
a = np.frombuffer(value, dtype=dtype)
return a.reshape(shape)


def to_sequence(value: list, shape: list, dtype: DType) -> Tensor:
""" Convert `VALUES` result from RedisAI to `Tensor` """
dtype = DType.__members__[dtype.lower()]
convert_to_num(dtype, value)
return Tensor(value, tuple(shape), dtype, 'VALUES')
2 changes: 1 addition & 1 deletion redisai/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '0.4.1'
__version__ = '0.5.0'
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#!/usr/bin/env python
from setuptools import setup, find_packages

Expand Down
Loading