nodev 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,474 @@
1
+
2
+ from nodev.utils import get_conv_outsize,pair
3
+ from nodev import cuda, as_datafield
4
+ import numpy as np
5
+
6
+ from nodev.core import Operation
7
+ from nodev.operations import broadcast_to
8
+
9
+ def im2col(x, kernel_h, kernel_w, stride=1, pad=0,to_matrix=False): #x 输入 kenrnel_size 卷积核大小 stride 步幅 pad 填充 to_matrix 能否变形为矩阵
10
+ xp = cuda.get_array_module(x)
11
+ N, C, H, W = x.shape
12
+
13
+ out_h = get_conv_outsize(H, kernel_h, stride, pad)
14
+ out_w = get_conv_outsize(W, kernel_w, stride, pad)
15
+
16
+ img = xp.pad(x,
17
+ [(0,0),(0,0),(pad,pad),(pad,pad)],
18
+ 'constant')
19
+
20
+ col = xp.zeros((N, C, kernel_h, kernel_w, out_h, out_w))
21
+
22
+ for y in range(kernel_h):
23
+ y_max = y + stride*out_h
24
+ for x in range(kernel_w):
25
+ x_max = x + stride*out_w
26
+
27
+ col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
28
+
29
+ col = col.transpose(0,4,5,1,2,3).reshape(N*out_h*out_w, -1)
30
+
31
+ return col
32
+ """def conv2d_simple(x,W,b=None,stride=1,pad=0):
33
+ x,W=as_variable(x),as_variable(W)
34
+ weight=W
35
+ N,C,H,W=x.shape
36
+ OC,C,KH,KW=weight.shape
37
+ SH,SW=pair(stride)
38
+ PH,PW=pair(pad)
39
+ OH=get_conv_outsize(H,KH,SH,PH)
40
+ OW=get_conv_outsize(W,KW,SW,PW)
41
+ col=im2col(x,(KH,KW),stride,pad,to_matrix=True)
42
+ weight=weight.reshape(OC,-1).transpose()
43
+ t=linear(col,weight,b)
44
+ y=t.reshape(N,OH,OW,OC).transpose(0,3,1,2)
45
+ return y"""
46
+
47
+
48
+ class Conv2d(Operation):
49
+ def __init__(self, stride=1, pad=0):
50
+ super().__init__()
51
+ self.stride = pair(stride)
52
+ self.pad = pair(pad)
53
+
54
+ def forward(self, x, W, b):
55
+ xp = cuda.get_array_module(x)
56
+
57
+ KH, KW = W.shape[2:]
58
+ col = im2col_array(x, (KH, KW), self.stride, self.pad, to_matrix=False)
59
+ #col = im2col_array(x, (KH, KW), self.stride, self.pad)
60
+ y = xp.tensordot(col, W, ((1, 2, 3), (1, 2, 3)))
61
+ if b is not None:
62
+ y += b
63
+ y = xp.rollaxis(y, 3, 1)
64
+ # y = np.transpose(y, (0, 3, 1, 2))
65
+ return y
66
+
67
+ def backward(self, gy):
68
+ x, W, b = self.inputs
69
+ # ==== gx ====
70
+ gx = deconv2d(gy, W, b=None, stride=self.stride, pad=self.pad,
71
+ outsize=(x.shape[2], x.shape[3]))
72
+ # ==== gW ====
73
+ gW = Conv2DGradW(self)(x, gy)
74
+ # ==== gb ====
75
+ gb = None
76
+ if b.data is not None:
77
+ gb = gy.sum(axis=(0, 2, 3))
78
+ return gx, gW, gb
79
+
80
+
81
+ def conv2d(x, W, b=None, stride=1, pad=0):
82
+ return Conv2d(stride, pad)(x, W, b)
83
+
84
+
85
+ class Deconv2d(Operation):
86
+ def __init__(self, stride=1, pad=0, outsize=None):
87
+ super().__init__()
88
+ self.stride = pair(stride)
89
+ self.pad = pair(pad)
90
+ self.outsize = outsize
91
+
92
+ def forward(self, x, W, b):
93
+ xp = cuda.get_array_module(x)
94
+
95
+ Weight = W
96
+ SH, SW = self.stride
97
+ PH, PW = self.pad
98
+ C, OC, KH, KW = Weight.shape
99
+ N, C, H, W = x.shape
100
+ if self.outsize is None:
101
+ out_h = get_deconv_outsize(H, KH, SH, PH)
102
+ out_w = get_deconv_outsize(W, KW, SW, PW)
103
+ else:
104
+ out_h, out_w = pair(self.outsize)
105
+ img_shape = (N, OC, out_h, out_w)
106
+
107
+ gcol = xp.tensordot(Weight, x, (0, 1))
108
+ gcol = xp.rollaxis(gcol, 3)
109
+ y = col2im_array(gcol, img_shape, (KH, KW), self.stride, self.pad,
110
+ to_matrix=False)
111
+ # b, k, h, w
112
+ if b is not None:
113
+ self.no_bias = True
114
+ y += b.reshape((1, b.size, 1, 1))
115
+ return y
116
+
117
+ def backward(self, gy):
118
+ x, W, b = self.inputs
119
+
120
+ # ==== gx ====
121
+ gx = conv2d(gy, W, b=None, stride=self.stride, pad=self.pad)
122
+ # ==== gW ====
123
+ f = Conv2DGradW(self)
124
+ gW = f(gy, x)
125
+ # ==== gb ====
126
+ gb = None
127
+ if b.data is not None:
128
+ gb = gy.sum(axis=(0, 2, 3))
129
+ return gx, gW, gb
130
+
131
+
132
+ def deconv2d(x, W, b=None, stride=1, pad=0, outsize=None):
133
+ return Deconv2d(stride, pad, outsize)(x, W, b)
134
+
135
+
136
+ class Conv2DGradW(Operation):
137
+ def __init__(self, conv2d):
138
+ W = conv2d.inputs[1]
139
+ kh, kw = W.shape[2:]
140
+ self.kernel_size = (kh, kw)
141
+ self.stride = conv2d.stride
142
+ self.pad = conv2d.pad
143
+
144
+ def forward(self, x, gy):
145
+ xp = cuda.get_array_module(x)
146
+
147
+ col = im2col_array(x, self.kernel_size, self.stride, self.pad,
148
+ to_matrix=False)
149
+ gW = xp.tensordot(gy, col, ((0, 2, 3), (0, 4, 5)))
150
+ return gW
151
+
152
+ def backward(self, gys):
153
+ x, gy = self.inputs
154
+ gW, = self.outputs
155
+
156
+ xh, xw = x.shape[2:]
157
+ gx = deconv2d(gy, gW, stride=self.stride, pad=self.pad,
158
+ outsize=(xh, xw))
159
+ ggy = conv2d(x, gW, stride=self.stride, pad=self.pad)
160
+ return gx, ggy
161
+
162
+
163
+ # =============================================================================
164
+ # pooling(max-pooling) / average_pooling
165
+ # =============================================================================
166
+ class Pooling(Operation):
167
+ def __init__(self, kernel_size, stride=1, pad=0):
168
+ super().__init__()
169
+ self.kernel_size = kernel_size
170
+ self.stride = stride
171
+ self.pad = pad
172
+
173
+ def forward(self, x):
174
+ col = im2col_array(x, self.kernel_size, self.stride, self.pad,
175
+ to_matrix=False)
176
+
177
+ N, C, KH, KW, OH, OW = col.shape
178
+ col = col.reshape(N, C, KH * KW, OH, OW)
179
+ self.indexes = col.argmax(axis=2)
180
+ y = col.max(axis=2)
181
+ return y
182
+
183
+ def backward(self, gy):
184
+ return Pooling2DGrad(self)(gy)
185
+
186
+
187
+ class Pooling2DGrad(Operation):
188
+ def __init__(self, mpool2d):
189
+ self.mpool2d = mpool2d
190
+ self.kernel_size = mpool2d.kernel_size
191
+ self.stride = mpool2d.stride
192
+ self.pad = mpool2d.pad
193
+ self.input_shape = mpool2d.inputs[0].shape
194
+ self.dtype = mpool2d.inputs[0].dtype
195
+ self.indexes = mpool2d.indexes
196
+
197
+ def forward(self, gy):
198
+ xp = cuda.get_array_module(gy)
199
+
200
+ N, C, OH, OW = gy.shape
201
+ N, C, H, W = self.input_shape
202
+ KH, KW = pair(self.kernel_size)
203
+
204
+ gcol = xp.zeros((N * C * OH * OW * KH * KW), dtype=self.dtype)
205
+
206
+ indexes = (self.indexes.ravel()
207
+ + xp.arange(0, self.indexes.size * KH * KW, KH * KW))
208
+
209
+ gcol[indexes] = gy.ravel()
210
+ gcol = gcol.reshape(N, C, OH, OW, KH, KW)
211
+ gcol = xp.swapaxes(gcol, 2, 4)
212
+ gcol = xp.swapaxes(gcol, 3, 5)
213
+
214
+ gx = col2im_array(gcol, (N, C, H, W), self.kernel_size, self.stride,
215
+ self.pad, to_matrix=False)
216
+ return gx
217
+
218
+ def backward(self, ggx):
219
+ f = Pooling2DWithIndexes(self.mpool2d)
220
+ return f(ggx)
221
+
222
+
223
+ class Pooling2DWithIndexes(Operation):
224
+ def __init__(self, mpool2d):
225
+ self.kernel_size = mpool2d.kernel_size
226
+ self.stride = mpool2d.stride
227
+ self.pad = mpool2d.pad
228
+ self.input_shpae = mpool2d.inputs[0].shape
229
+ self.dtype = mpool2d.inputs[0].dtype
230
+ self.indexes = mpool2d.indexes
231
+
232
+ def forward(self, x):
233
+ col = im2col_array(x, self.kernel_size, self.stride, self.pad,
234
+ to_matrix=False)
235
+ N, C, KH, KW, OH, OW = col.shape
236
+ col = col.reshape(N, C, KH * KW, OH, OW)
237
+ col = col.transpose(0, 1, 3, 4, 2).reshape(-1, KH * KW)
238
+ indexes = self.indexes.ravel()
239
+ col = col[np.arange(len(indexes)), indexes]
240
+ return col.reshape(N, C, OH, OW)
241
+
242
+
243
+ def pooling(x, kernel_size, stride=1, pad=0):
244
+ return Pooling(kernel_size, stride, pad)(x)
245
+
246
+
247
+ class AveragePooling(Operation):
248
+ def __init__(self, kernel_size, stride=1, pad=0):
249
+ super().__init__()
250
+ self.kernel_size = kernel_size
251
+ self.stride = stride
252
+ self.pad = pad
253
+ self.input_shape = None
254
+
255
+ def forward(self, x):
256
+ self.input_shape = x.shape
257
+ col = im2col_array(x, self.kernel_size, self.stride, self.pad,
258
+ to_matrix=False)
259
+ y = col.mean(axis=(2, 3))
260
+ return y
261
+
262
+ def backward(self, gy):
263
+ # TODO(Koki): This is simple implementation
264
+ N, C, OH, OW = gy.shape
265
+ KW, KH = pair(self.kernel_size)
266
+ gy /= (KW * KH)
267
+ gcol = broadcast_to(gy.reshape(-1), (KH, KW, N * C * OH * OW))
268
+ gcol = gcol.reshape(KH, KW, N, C, OH, OW).transpose(2, 3, 0, 1, 4, 5)
269
+ gx = col2im(gcol, self.input_shape, self.kernel_size, self.stride,
270
+ self.pad, to_matrix=False)
271
+ return gx
272
+
273
+
274
+ def average_pooling(x, kernel_size, stride=1, pad=0):
275
+ return AveragePooling(kernel_size, stride, pad)(x)
276
+
277
+
278
+ # =============================================================================
279
+ # im2col / col2im
280
+ # =============================================================================
281
+ class Im2col(Operation):
282
+ def __init__(self, kernel_size, stride, pad, to_matrix):
283
+ super().__init__()
284
+ self.input_shape = None
285
+ self.kernel_size = kernel_size
286
+ self.stride = stride
287
+ self.pad = pad
288
+ self.to_matrix = to_matrix
289
+
290
+ def forward(self, x):
291
+ self.input_shape = x.shape
292
+ y = im2col_array(x, self.kernel_size, self.stride, self.pad,
293
+ self.to_matrix)
294
+ return y
295
+
296
+ def backward(self, gy):
297
+ gx = col2im(gy, self.input_shape, self.kernel_size, self.stride,
298
+ self.pad, self.to_matrix)
299
+ return gx
300
+
301
+
302
+
303
+ class Col2im(Operation):
304
+ def __init__(self, input_shape, kernel_size, stride, pad, to_matrix):
305
+ super().__init__()
306
+ self.input_shape = input_shape
307
+ self.kernel_size = kernel_size
308
+ self.stride = stride
309
+ self.pad = pad
310
+ self.to_matrix = to_matrix
311
+
312
+ def forward(self, x):
313
+ y = col2im_array(x, self.input_shape, self.kernel_size, self.stride,
314
+ self.pad, self.to_matrix)
315
+ return y
316
+
317
+ def backward(self, gy):
318
+ gx = im2col(gy, self.kernel_size, self.stride, self.pad,
319
+ self.to_matrix)
320
+ return gx
321
+
322
+
323
+ def col2im(x, input_shape, kernel_size, stride=1, pad=0, to_matrix=True):
324
+ return Col2im(input_shape, kernel_size, stride, pad, to_matrix)(x)
325
+
326
+
327
+ # =============================================================================
328
+ # numpy im2col
329
+ # =============================================================================
330
+ def im2col_array(img, kernel_size, stride, pad, to_matrix=True):
331
+ N, C, H, W = img.shape
332
+ KH, KW = pair(kernel_size)
333
+ SH, SW = pair(stride)
334
+ PH, PW = pair(pad)
335
+ OH = get_conv_outsize(H, KH, SH, PH)
336
+ OW = get_conv_outsize(W, KW, SW, PW)
337
+
338
+ xp = cuda.get_array_module(img)
339
+ if xp != np:
340
+ col = _im2col_gpu(img, kernel_size, stride, pad)
341
+ else:
342
+ img = np.pad(img,
343
+ ((0, 0), (0, 0), (PH, PH + SH - 1), (PW, PW + SW - 1)),
344
+ mode='constant', constant_values=(0,))
345
+ col = np.ndarray((N, C, KH, KW, OH, OW), dtype=img.dtype)
346
+
347
+ for j in range(KH):
348
+ j_lim = j + SH * OH
349
+ for i in range(KW):
350
+ i_lim = i + SW * OW
351
+ col[:, :, j, i, :, :] = img[:, :, j:j_lim:SH, i:i_lim:SW]
352
+
353
+ if to_matrix:
354
+ col = col.transpose((0, 4, 5, 1, 2, 3)).reshape((N * OH * OW, -1))
355
+
356
+ return col
357
+
358
+
359
+ def col2im_array(col, img_shape, kernel_size, stride, pad, to_matrix=True):
360
+ N, C, H, W = img_shape
361
+ KH, KW = pair(kernel_size)
362
+ SH, SW = pair(stride)
363
+ PH, PW = pair(pad)
364
+ OH = get_conv_outsize(H, KH, SH, PH)
365
+ OW = get_conv_outsize(W, KW, SW, PW)
366
+
367
+ if to_matrix:
368
+ col = col.reshape(N, OH, OW, C, KH, KW).transpose(0, 3, 4, 5, 1, 2)
369
+
370
+ xp = cuda.get_array_module(col)
371
+ if xp != np:
372
+ img = _col2im_gpu(col, SH, SW, PH, PW, H, W)
373
+ return img
374
+ else:
375
+ img = np.zeros((N, C, H + 2 * PH + SH - 1, W + 2 * PW + SW - 1),
376
+ dtype=col.dtype)
377
+ for j in range(KH):
378
+ j_lim = j + SH * OH
379
+ for i in range(KW):
380
+ i_lim = i + SW * OW
381
+ img[:, :, j:j_lim:SH, i:i_lim:SW] += col[:, :, j, i, :, :]
382
+ return img[:, :, PH:H + PH, PW:W + PW]
383
+
384
+
385
+ def _im2col_gpu(img, kernel_size, stride, pad):
386
+ """im2col function for GPU.
387
+ This code is ported from Chainer:
388
+ https://github.com/chainer/chainer/blob/v6.4.0/chainer/utils/conv.py
389
+ """
390
+ n, c, h, w = img.shape
391
+ kh, kw = pair(kernel_size)
392
+ sy, sx = pair(stride)
393
+ ph, pw = pair(pad)
394
+ out_h = get_conv_outsize(h, kh, sy, ph)
395
+ out_w = get_conv_outsize(w, kw, sx, pw)
396
+ dy, dx = 1, 1
397
+ col = cuda.cupy.empty((n, c, kh, kw, out_h, out_w), dtype=img.dtype)
398
+
399
+ cuda.cupy.ElementwiseKernel(
400
+ 'raw T img, int32 h, int32 w, int32 out_h, int32 out_w,'
401
+ 'int32 kh, int32 kw, int32 sy, int32 sx, int32 ph, int32 pw,'
402
+ 'int32 dy, int32 dx',
403
+ 'T col',
404
+ '''
405
+ int c0 = i / (kh * kw * out_h * out_w);
406
+ int ky = i / (kw * out_h * out_w) % kh;
407
+ int kx = i / (out_h * out_w) % kw;
408
+ int out_y = i / out_w % out_h;
409
+ int out_x = i % out_w;
410
+ int in_y = ky * dy + out_y * sy - ph;
411
+ int in_x = kx * dx + out_x * sx - pw;
412
+ if (in_y >= 0 && in_y < h && in_x >= 0 && in_x < w) {
413
+ col = img[in_x + w * (in_y + h * c0)];
414
+ } else {
415
+ col = 0;
416
+ }
417
+ ''',
418
+ 'im2col')(img.reduced_view(),
419
+ h, w, out_h, out_w, kh, kw, sy, sx, ph, pw, dy, dx, col)
420
+
421
+ return col
422
+ def get_deconv_outsize(size, k, stride, pad):
423
+ return stride * (size - 1) + k - 2 * pad
424
+ def _col2im_gpu(col, sy, sx, ph, pw, h, w):
425
+ """col2im function for GPU.
426
+ This code is ported from Chainer:
427
+ https://github.com/chainer/chainer/blob/v6.4.0/chainer/utils/conv.py
428
+ """
429
+ n, c, kh, kw, out_h, out_w = col.shape
430
+ dx, dy = 1, 1
431
+ img = cuda.cupy.empty((n, c, h, w), dtype=col.dtype)
432
+
433
+ cuda.cupy.ElementwiseKernel(
434
+ 'raw T col, int32 h, int32 w, int32 out_h, int32 out_w,'
435
+ 'int32 kh, int32 kw, int32 sy, int32 sx, int32 ph, int32 pw,'
436
+ 'int32 dx, int32 dy',
437
+ 'T img',
438
+ '''
439
+ int c0 = i / (h * w);
440
+ int y = i / w % h;
441
+ int x = i % w;
442
+ T val = 0;
443
+ for (int ky = 0; ky < kh; ++ky) {
444
+ int out_y = (y + ph - ky * dy);
445
+ if (0 > out_y || out_y >= out_h * sy) continue;
446
+ if (out_y % sy != 0) continue;
447
+ out_y /= sy;
448
+ for (int kx = 0; kx < kw; ++kx) {
449
+ int out_x = (x + pw - kx * dx);
450
+ if (0 > out_x || out_x >= out_w * sx) continue;
451
+ if (out_x % sx != 0) continue;
452
+ out_x /= sx;
453
+ int k = out_y + out_h * (kx + kw * (ky + kh * c0));
454
+ val = val + col[out_x + out_w * k];
455
+ }
456
+ }
457
+ img = val;
458
+ ''',
459
+ 'col2im')(col.reduced_view(),
460
+ h, w, out_h, out_w, kh, kw, sy, sx, ph, pw, dx, dy, img)
461
+ return img
462
+ def pooling_simple(x,kernel_size,stride=1,pad=0):
463
+ x=as_datafield(x)
464
+ N,C,H,W=x.shape
465
+ KH,KW=pair(kernel_size)
466
+ PH,PW=pair(pad)
467
+ SH,SW=pair(stride)
468
+ OH=get_conv_outsize(H,KH,SH,PH)
469
+ OW=get_conv_outsize(W,KW,SW,PW)
470
+ col=im2col(x,kernel_size,stride,pad,to_matrix=True)
471
+ col=col.reshape(-1,KH*KW)
472
+ y=col.max(axis=1)
473
+ y=y.reshape((N,OH,OW,C)).transpose(0,3,1,2)
474
+ return y
nodev/optimizer.py ADDED
@@ -0,0 +1,72 @@
1
+
2
+ import math
3
+ from nodev import cuda
4
+ from nodev.cuda import get_array_module
5
+
6
+ class Optimizer:
7
+ def __init__(self):
8
+ self.target=None
9
+ self.hooks=[]
10
+ def setup(self,target):
11
+ self.target=target
12
+ return self
13
+ def update(self):
14
+ params=[p for p in self.target.params() if p.grad is not None]
15
+ #预处理
16
+ for f in self.hooks:
17
+ f(params)
18
+ #更新参数
19
+ for param in params:
20
+ self.update_one(param)
21
+ def update_one(self,param):
22
+ raise NotImplementedError
23
+ def add_hook(self,f):
24
+ self.hooks.append(f)
25
+
26
+ class SGD(Optimizer):
27
+ def __init__(self,lr=0.01):
28
+ super().__init__()
29
+ self.lr=lr
30
+
31
+ def update_one(self,param):
32
+ param.data-=self.lr*param.grad.data
33
+
34
+ class MomentumSGD(Optimizer):
35
+ def __init__(self,lr=0.01,momentum=0.9):
36
+ super().__init__()
37
+ self.lr=lr
38
+ self.momentum=momentum
39
+ self.vs={}
40
+ def update_one(self,param):
41
+ v_key=id(param)
42
+ if v_key not in self.vs:
43
+ xp = get_array_module(param.data)
44
+ self.vs[v_key] = xp.zeros_like(param.data)
45
+
46
+ v=self.vs[v_key]
47
+ v*=self.momentum
48
+ v-=self.lr*param.grad.data
49
+ param.data += v
50
+
51
+ class AdaGrad(Optimizer):
52
+ def __init__(self, lr=0.001, eps=1e-8):
53
+ super().__init__()
54
+ self.lr = lr
55
+ self.eps = eps
56
+ self.hs = {}
57
+
58
+ def update_one(self, param):
59
+ xp = cuda.get_array_module(param.data)
60
+
61
+ h_key = id(param)
62
+ if h_key not in self.hs:
63
+ self.hs[h_key] = xp.zeros_like(param.data)
64
+
65
+ lr = self.lr
66
+ eps = self.eps
67
+ grad = param.grad.data
68
+ h = self.hs[h_key]
69
+
70
+ h += grad * grad
71
+ param.data -= lr * grad / (xp.sqrt(h) + eps)
72
+
nodev/transforms.py ADDED
@@ -0,0 +1,65 @@
1
+ import numpy as np
2
+
3
+
4
+ class Compose:
5
+ """Compose multiple transforms."""
6
+
7
+ def __init__(self, transforms=[]):
8
+ self.transforms = transforms
9
+
10
+ def __call__(self, x):
11
+ if not self.transforms:
12
+ return x
13
+ for t in self.transforms:
14
+ x = t(x)
15
+ return x
16
+
17
+
18
+ class Flatten:
19
+ """Flatten ndarray."""
20
+
21
+ def __call__(self, x):
22
+ return x.reshape(-1)
23
+
24
+
25
+ class ToFloat:
26
+ """Convert ndarray dtype to float32."""
27
+
28
+ def __call__(self, x):
29
+ return x.astype(np.float32)
30
+
31
+
32
+ class Normalize:
33
+ """Normalize ndarray."""
34
+
35
+ def __init__(self, mean=0., std=1.):
36
+ self.mean = mean
37
+ self.std = std
38
+
39
+ def __call__(self, x):
40
+ mean = self.mean
41
+ std = self.std
42
+
43
+ if not np.isscalar(mean):
44
+ mean = np.array(mean, dtype=x.dtype)
45
+ if not np.isscalar(std):
46
+ std = np.array(std, dtype=x.dtype)
47
+
48
+ return (x - mean) / std
49
+
50
+
51
+ class AsType:
52
+ """Convert ndarray dtype."""
53
+
54
+ def __init__(self, dtype=np.float32):
55
+ self.dtype = dtype
56
+
57
+ def __call__(self, x):
58
+ return x.astype(self.dtype)
59
+
60
+
61
+ class ToInt:
62
+ """Convert ndarray dtype to int."""
63
+
64
+ def __call__(self, x):
65
+ return x.astype(np.int32)