Skip to content

Commit 18837ea

Browse files
Sherin Thomaslantiga
Sherin Thomas
authored andcommitted
New version (#27)
* new tensor APIs * container namedtuples * tflite * simplified APIs stabilization init * test cases fix * todo fixes, LGTM.com fixes, type annotations * more tests * supporting np dtypes
1 parent 94d0499 commit 18837ea

11 files changed

+170
-228
lines changed

example.py

+10-16
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,26 @@
1-
from redisai import Client, Tensor, \
2-
BlobTensor, DType, Device, Backend
1+
import numpy as np
2+
from redisai import Client, DType, Device, Backend
33
import ml2rt
44

55
client = Client()
6-
client.tensorset('x', Tensor(DType.float, [2], [2, 3]))
6+
client.tensorset('x', [2, 3], dtype=DType.float)
77
t = client.tensorget('x')
88
print(t.value)
99

1010
model = ml2rt.load_model('test/testdata/graph.pb')
11-
client.tensorset('a', Tensor.scalar(DType.float, 2, 3))
12-
client.tensorset('b', Tensor.scalar(DType.float, 12, 10))
11+
tensor1 = np.array([2, 3], dtype=np.float)
12+
client.tensorset('a', tensor1)
13+
client.tensorset('b', (12, 10), dtype=np.float)
1314
client.modelset('m', Backend.tf,
1415
Device.cpu,
15-
input=['a', 'b'],
16-
output='mul',
16+
inputs=['a', 'b'],
17+
outputs='mul',
1718
data=model)
1819
client.modelrun('m', ['a', 'b'], ['mul'])
19-
print(client.tensorget('mul').value)
20+
print(client.tensorget('mul'))
2021

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

26-
b1 = client.tensorget('c', as_type=BlobTensor)
27-
b2 = client.tensorget('c', as_type=BlobTensor)
28-
29-
client.tensorset('d', BlobTensor(DType.float, b1.shape, b1, b2))
30-
31-
tnp = b1.to_numpy()
32-
client.tensorset('e', tnp)

redisai/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
from .version import __version__
22
from .client import Client
3-
from .tensor import Tensor, BlobTensor
43
from .constants import DType, Device, Backend

redisai/client.py

+35-40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from redis import StrictRedis
2-
from typing import Union, Any, AnyStr, ByteString, Sequence, Type
2+
from typing import Union, Any, AnyStr, ByteString, Sequence
3+
from .containers import Script, Model, Tensor
34

45
try:
56
import numpy as np
@@ -8,7 +9,7 @@
89

910
from .constants import Backend, Device, DType
1011
from .utils import str_or_strsequence, to_string
11-
from .tensor import Tensor, BlobTensor
12+
from . import convert
1213

1314

1415
class Client(StrictRedis):
@@ -45,13 +46,12 @@ def modelset(self,
4546
args += [data]
4647
return self.execute_command(*args)
4748

48-
def modelget(self, name: AnyStr) -> dict:
49+
def modelget(self, name: AnyStr) -> Model:
4950
rv = self.execute_command('AI.MODELGET', name)
50-
return {
51-
'backend': Backend(to_string(rv[0])),
52-
'device': Device(to_string(rv[1])),
53-
'data': rv[2]
54-
}
51+
return Model(
52+
rv[2],
53+
Device(to_string(rv[1])),
54+
Backend(to_string(rv[0])))
5555

5656
def modeldel(self, name: AnyStr) -> AnyStr:
5757
return self.execute_command('AI.MODELDEL', name)
@@ -68,71 +68,66 @@ def modelrun(self,
6868

6969
def tensorset(self,
7070
key: AnyStr,
71-
tensor: Union[Tensor, np.ndarray, list, tuple],
71+
tensor: Union[np.ndarray, list, tuple],
7272
shape: Union[Sequence[int], None] = None,
73-
dtype: Union[DType, None] = None) -> Any:
73+
dtype: Union[DType, type, None] = None) -> Any:
7474
"""
7575
Set the values of the tensor on the server using the provided Tensor object
7676
:param key: The name of the tensor
77-
:param tensor: a `Tensor` object
78-
:param shape: Shape of the tensor
79-
:param dtype: data type of the tensor. Required if input is a sequence of ints/floats
77+
:param tensor: a `np.ndarray` object or python list or tuple
78+
:param shape: Shape of the tensor. Required if `tensor` is list or tuple
79+
:param dtype: data type of the tensor. Required if `tensor` is list or tuple
8080
"""
81-
# TODO: tensorset will not accept BlobTensor or Tensor object in the future.
82-
# Keeping it in the current version for compatibility with the example repo
8381
if np and isinstance(tensor, np.ndarray):
84-
tensor = BlobTensor.from_numpy(tensor)
82+
tensor = convert.from_numpy(tensor)
83+
args = ['AI.TENSORSET', key, tensor.dtype.value, *tensor.shape, tensor.argname, tensor.value]
8584
elif isinstance(tensor, (list, tuple)):
8685
if shape is None:
8786
shape = (len(tensor),)
88-
tensor = Tensor(dtype, shape, tensor)
89-
args = ['AI.TENSORSET', key, tensor.type.value]
90-
args += tensor.shape
91-
args += [tensor.ARGNAME]
92-
args += tensor.value
87+
if not isinstance(dtype, DType):
88+
dtype = DType.__members__[np.dtype(dtype).name]
89+
tensor = convert.from_sequence(tensor, shape, dtype)
90+
args = ['AI.TENSORSET', key, tensor.dtype.value, *tensor.shape, tensor.argname, *tensor.value]
9391
return self.execute_command(*args)
9492

9593
def tensorget(self,
96-
key: AnyStr, as_type: Type[Tensor] = None,
97-
meta_only: bool = False) -> Union[Tensor, BlobTensor]:
94+
key: AnyStr, as_numpy: bool = True,
95+
meta_only: bool = False) -> Union[Tensor, np.ndarray]:
9896
"""
9997
Retrieve the value of a tensor from the server. By default it returns the numpy array
10098
but it can be controlled using `as_type` argument and `meta_only` argument.
10199
:param key: the name of the tensor
102-
:param as_type: the resultant tensor type. Returns numpy array if None
100+
:param as_numpy: Should it return data as numpy.ndarray.
101+
Wraps with namedtuple if False. This flag also decides how to fetch the
102+
value from RedisAI server and could have performance implications
103103
:param meta_only: if true, then the value is not retrieved,
104104
only the shape and the type
105105
:return: an instance of as_type
106106
"""
107-
# TODO; We might remove Tensor & BlobTensor in the future and `tensorget` will return
108-
# python list or numpy arrays or a namedtuple
109107
if meta_only:
110108
argname = 'META'
111-
elif as_type is None:
112-
argname = BlobTensor.ARGNAME
109+
elif as_numpy is True:
110+
argname = 'BLOB'
113111
else:
114-
argname = as_type.ARGNAME
112+
argname = 'VALUES'
115113

116114
res = self.execute_command('AI.TENSORGET', key, argname)
117115
dtype, shape = to_string(res[0]), res[1]
118-
dt = DType.__members__[dtype.lower()]
119116
if meta_only:
120-
return Tensor(dt, shape, [])
121-
elif as_type is None:
122-
return BlobTensor.from_resp(dt, shape, res[2]).to_numpy()
117+
return convert.to_sequence([], shape, dtype)
118+
if as_numpy is True:
119+
return convert.to_numpy(res[2], shape, dtype)
123120
else:
124-
return as_type.from_resp(dt, shape, res[2])
121+
return convert.to_sequence(res[2], shape, dtype)
125122

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

129-
def scriptget(self, name: AnyStr) -> dict:
126+
def scriptget(self, name: AnyStr) -> Script:
130127
r = self.execute_command('AI.SCRIPTGET', name)
131-
device = Device(to_string(r[0]))
132-
return {
133-
'device': device,
134-
'script': to_string(r[1])
135-
}
128+
return Script(
129+
to_string(r[1]),
130+
Device(to_string(r[0])))
136131

137132
def scriptdel(self, name):
138133
return self.execute_command('AI.SCRIPTDEL', name)

redisai/constants.py

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Backend(Enum):
1010
tf = 'TF'
1111
torch = 'TORCH'
1212
onnx = 'ONNX'
13+
tflite = 'TFLITE'
1314

1415

1516
class DType(Enum):

redisai/containers.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from collections import namedtuple
2+
3+
Tensor = namedtuple('Tensor', field_names=['value', 'shape', 'dtype', 'argname'])
4+
Script = namedtuple('Script', field_names=['script', 'device'])
5+
Model = namedtuple('Model', field_names=['data', 'device', 'backend'])

redisai/convert.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from typing import Union, ByteString, Sequence
2+
from .utils import convert_to_num
3+
from .constants import DType
4+
from .containers import Tensor
5+
try:
6+
import numpy as np
7+
except (ImportError, ModuleNotFoundError):
8+
np = None
9+
10+
11+
def from_numpy(tensor: np.ndarray) -> Tensor:
12+
""" Convert the numpy input from user to `Tensor` """
13+
dtype = DType.__members__[str(tensor.dtype)]
14+
shape = tensor.shape
15+
blob = bytes(tensor.data)
16+
return Tensor(blob, shape, dtype, 'BLOB')
17+
18+
19+
def from_sequence(tensor: Sequence, shape: Union[list, tuple], dtype: DType) -> Tensor:
20+
""" Convert the `list`/`tuple` input from user to `Tensor` """
21+
return Tensor(tensor, shape, dtype, 'VALUES')
22+
23+
24+
def to_numpy(value: ByteString, shape: Union[list, tuple], dtype: DType) -> np.ndarray:
25+
""" Convert `BLOB` result from RedisAI to `np.ndarray` """
26+
dtype = DType.__members__[dtype.lower()].value
27+
mm = {
28+
'FLOAT': 'float32',
29+
'DOUBLE': 'float64'
30+
}
31+
if dtype in mm:
32+
dtype = mm[dtype]
33+
else:
34+
dtype = dtype.lower()
35+
a = np.frombuffer(value, dtype=dtype)
36+
return a.reshape(shape)
37+
38+
39+
def to_sequence(value: list, shape: list, dtype: DType) -> Tensor:
40+
""" Convert `VALUES` result from RedisAI to `Tensor` """
41+
dtype = DType.__members__[dtype.lower()]
42+
convert_to_num(dtype, value)
43+
return Tensor(value, tuple(shape), dtype, 'VALUES')

redisai/tensor.py

-120
This file was deleted.

redisai/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
# 1) we don't load dependencies by storing it in __init__.py
33
# 2) we can import it in setup.py for the same reason
44
# 3) we can import it into your module module
5-
__version__ = '0.4.1'
5+
__version__ = '0.5.0'

setup.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#!/usr/bin/env python
32
from setuptools import setup, find_packages
43

0 commit comments

Comments
 (0)