1
- from redis import StrictRedis
2
- from redis ._compat import (long , nativestr )
3
1
from enum import Enum
4
- import six
5
-
6
- class Type (Enum ):
7
- FLOAT = 1
8
- DOUBLE = 2
9
- INT8 = 3
10
- INT16 = 4
11
- INT32 = 5
12
- INT64 = 6
13
- UINT8 = 7
14
- UINT16 = 8
15
-
2
+ from redis import StrictRedis
3
+ from ._util import to_string
4
+
5
+ try :
6
+ import numpy as np
7
+ except ImportError :
8
+ np = None
9
+
10
+ try :
11
+ from typing import Union , Any , AnyStr , ByteString , Collection
12
+ except ImportError :
13
+ pass
14
+
15
+
16
+ class Device (Enum ):
17
+ cpu = 'cpu'
18
+ gpu = 'gpu'
19
+
20
+
21
+ class Backend (Enum ):
22
+ tf = 'tf'
23
+ torch = 'torch'
24
+ onnx = 'ort'
25
+
26
+
27
+ class DType (Enum ):
28
+ float = 'float'
29
+ double = 'double'
30
+ int8 = 'int8'
31
+ int16 = 'int16'
32
+ int32 = 'int32'
33
+ int64 = 'int64'
34
+ uint8 = 'uint8'
35
+ uint16 = 'uint16'
36
+ uint32 = 'uint32'
37
+ uint64 = 'uint64'
38
+
39
+ # aliases
40
+ float32 = 'float'
41
+ float64 = 'double'
16
42
17
- class Client (StrictRedis ):
18
43
19
- def __init__ (self , * args , ** kwargs ):
44
+ class Tensor (object ):
45
+ ARGNAME = 'VALUES'
46
+
47
+ def __init__ (self ,
48
+ dtype , # type: DType
49
+ shape , # type: Collection[int]
50
+ value ):
51
+ """
52
+ Declare a tensor suitable for passing to tensorset
53
+ :param dtype: The type the values should be stored as.
54
+ This can be one of Tensor.FLOAT, tensor.DOUBLE, etc.
55
+ :param shape: An array describing the shape of the tensor. For an
56
+ image 250x250 with three channels, this would be [250, 250, 3]
57
+ :param value: The value for the tensor. Can be an array.
58
+ The contents must coordinate with the shape, meaning that the
59
+ overall length needs to be the product of all figures in the
60
+ shape. There is no verification to ensure that each dimension
61
+ is correct. Your application must ensure that the ordering
62
+ is always consistent.
20
63
"""
21
- Create a new Client optional host and port
64
+ self .type = dtype
65
+ self .shape = shape
66
+ self .value = value
67
+ self ._size = 1
68
+ if not isinstance (value , (list , tuple )):
69
+ self .value = [value ]
70
+
71
+ @property
72
+ def size (self ):
73
+ return self ._size
74
+
75
+ def __repr__ (self ):
76
+ return '<{c.__class__.__name__}(shape={s} type={t}) at 0x{id:x}>' .format (
77
+ c = self ,
78
+ s = self .shape ,
79
+ t = self .type ,
80
+ id = id (self ))
81
+
82
+
83
+ class ScalarTensor (Tensor ):
84
+ def __init__ (self , dtype , * values ):
85
+ # type: (ScalarTensor, DType, Any) -> None
86
+ """
87
+ Declare a tensor with a bunch of scalar values. This can be used
88
+ to 'batch-load' several tensors.
89
+
90
+ :param dtype: The datatype to store the tensor as
91
+ :param values: List of values
92
+ """
93
+ super (ScalarTensor , self ).__init__ (dtype , [1 ], values )
94
+ self ._size = len (values )
95
+
96
+
97
+ class BlobTensor (Tensor ):
98
+ ARGNAME = 'BLOB'
22
99
23
- If conn is not None, we employ an already existing redis connection
100
+ def __init__ (self ,
101
+ dtype ,
102
+ shape , # type: Collection[int]
103
+ * blobs # type: Union[BlobTensor, ByteString]
104
+ ):
24
105
"""
25
- StrictRedis .__init__ (self , * args , ** kwargs )
26
-
27
- # Set the module commands' callbacks
28
- MODULE_CALLBACKS = {
29
- 'AI.TENSORSET' : lambda r : r and nativestr (r ) == 'OK' ,
106
+ Create a tensor from a binary blob
107
+ :param dtype: The datatype, one of Tensor.FLOAT, Tensor.DOUBLE, etc.
108
+ :param shape: An array
109
+ :param blobs: One or more blobs to assign to the tensor.
110
+ """
111
+ if len (blobs ) > 1 :
112
+ blobarr = bytearray ()
113
+ for b in blobs :
114
+ if isinstance (b , BlobTensor ):
115
+ b = b .value [0 ]
116
+ blobarr += b
117
+ size = len (blobs )
118
+ blobs = bytes (blobarr )
119
+ else :
120
+ blobs = bytes (blobs [0 ])
121
+ size = 1
122
+
123
+ super (BlobTensor , self ).__init__ (dtype , shape , blobs )
124
+ self ._size = size
125
+
126
+ @classmethod
127
+ def from_numpy (cls , * nparrs ):
128
+ # type: (type, np.array) -> BlobTensor
129
+ blobs = []
130
+ for arr in nparrs :
131
+ blobs .append (arr .data )
132
+ dt = DType .__members__ [str (nparrs [0 ].dtype )]
133
+ return cls (dt , nparrs [0 ].shape , * blobs )
134
+
135
+ @property
136
+ def blob (self ):
137
+ return self .value [0 ]
138
+
139
+ def to_numpy (self ):
140
+ # type: () -> np.array
141
+ a = np .frombuffer (self .value [0 ], dtype = self ._to_numpy_type (self .type ))
142
+ return a .reshape (self .shape )
143
+
144
+ @staticmethod
145
+ def _to_numpy_type (t ):
146
+ t = t .lower ()
147
+ mm = {
148
+ 'float' : 'float32' ,
149
+ 'double' : 'float64'
150
+ }
151
+ if t in mm :
152
+ return mm [t ]
153
+ return t
154
+
155
+
156
+ class Client (StrictRedis ):
157
+ def modelset (self ,
158
+ name , # type: AnyStr
159
+ backend , # type: Backend
160
+ device , # type: Device
161
+ inputs , # type: Collection[AnyStr]
162
+ outputs , # type: Collection[AnyStr]
163
+ data # type: ByteString
164
+ ):
165
+ args = ['AI.MODELSET' , name , backend .value , device .value , 'INPUTS' ]
166
+ args += inputs
167
+ args += ['OUTPUTS' ] + outputs
168
+ args += [data ]
169
+ return self .execute_command (* args )
170
+
171
+ def modelget (self , name ):
172
+ rv = self .execute_command ('AI.MODELGET' , name )
173
+ return {
174
+ 'backend' : Backend (rv [0 ]),
175
+ 'device' : Device (rv [1 ]),
176
+ 'data' : rv [2 ]
30
177
}
31
- for k , v in six .iteritems (MODULE_CALLBACKS ):
32
- self .set_response_callback (k , v )
33
178
179
+ def modelrun (self , name , inputs , outputs ):
180
+ args = ['AI.MODELRUN' , name ]
181
+ args += ['INPUTS' ] + inputs + ['OUTPUTS' ] + outputs
182
+ return self .execute_command (* args )
34
183
35
- def tensorset (self , key , type , dimensions , tensor ):
36
- args = ['AI.TENSORSET' , key , type .name ] + dimensions + ['VALUES' ] + tensor
37
-
184
+ def tensorset (self , key , tensor ):
185
+ # type: (Client, AnyStr, Union[Tensor, np.ndarray]) -> Any
186
+ """
187
+ Set the values of the tensor on the server using the provided Tensor object
188
+ :param key: The name of the tensor
189
+ :param tensor: a `Tensor` object
190
+ """
191
+ if np and isinstance (tensor , np .ndarray ):
192
+ tensor = BlobTensor .from_numpy (tensor )
193
+ args = ['AI.TENSORSET' , key , tensor .type .value , tensor .size ]
194
+ args += tensor .shape
195
+ args += [tensor .ARGNAME ]
196
+ args += tensor .value
38
197
return self .execute_command (* args )
39
198
40
-
199
+ def tensorget (self , key , astype = Tensor , meta_only = False ):
200
+ """
201
+ Retrieve the value of a tensor from the server
202
+ :param key: the name of the tensor
203
+ :param astype: the resultant tensor type
204
+ :param meta_only: if true, then the value is not retrieved,
205
+ only the shape and the type
206
+ :return: an instance of astype
207
+ """
208
+ argname = 'META' if meta_only else astype .ARGNAME
209
+ res = self .execute_command ('AI.TENSORGET' , key , argname )
210
+ dtype , shape = to_string (res [0 ]), res [1 ]
211
+ if meta_only :
212
+ return astype (dtype , shape , [])
213
+ else :
214
+ return astype (dtype , shape , res [2 ])
215
+
216
+ def scriptset (self , name , device , script ):
217
+ return self .execute_command ('AI.SCRIPTSET' , name , device .value , script )
218
+
219
+ def scriptget (self , name ):
220
+ r = self .execute_command ('AI.SCRIPTGET' , name )
221
+ return {
222
+ 'device' : to_string (r [0 ]),
223
+ 'script' : to_string (r [1 ])
224
+ }
225
+
226
+ def scriptrun (self , name , function , inputs , outputs ):
227
+ args = ['AI.SCRIPTRUN' , name , function , 'INPUTS' ]
228
+ args += inputs
229
+ args += ['OUTPUTS' ]
230
+ args += outputs
231
+ return self .execute_command (* args )
0 commit comments