Skip to content

Commit 0797fb1

Browse files
author
yxdragon
committed
hello
1 parent d6fc59f commit 0797fb1

22 files changed

Lines changed: 437 additions & 815 deletions

cnnumpy/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .layer import *
2+
from .net import Net, read_net

cnnumpy/layer.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from .util import conv, maxpool, upsample
2+
import numpy as np
3+
4+
class Layer:
5+
name = 'layer'
6+
def __init__(self, name):
7+
self.name = name
8+
9+
def forward(self, x): pass
10+
11+
def backward(self, grad_y): pass
12+
13+
def para(self): return None
14+
15+
def __call__(self, x):
16+
return self.forward(x)
17+
18+
class Dense(Layer):
19+
name = 'dense'
20+
def __init__(self, c, n):
21+
self.K = np.zeros((n, c), dtype=np.float32)
22+
self.bias = np.zeros(c, dtype=np.float32)
23+
24+
def para(self): return self.K.shape
25+
26+
def forward(self, x):
27+
y = x.dot(self.K.T)
28+
y += self.bias.reshape((1, -1))
29+
return y
30+
31+
def load(self, weights, bias):
32+
self.K = weights.copy()
33+
self.bias = bias.copy()
34+
35+
class Conv2d(Layer):
36+
name = 'conv'
37+
def __init__(self, c, n, w, s):
38+
self.n, self.c, self.w, self.s = n, c, w, s
39+
self.K = np.zeros((n, c, w, w), dtype=np.float32)
40+
self.bias = np.zeros(n, dtype=np.float32)
41+
42+
def para(self): return self.n, self.c, self.w, self.s
43+
44+
def forward(self, x):
45+
out = conv(x, self.K, (self.s, self.s))
46+
out += self.bias.reshape((1, -1, 1, 1))
47+
return out
48+
49+
def load(self, weights, bias):
50+
self.K = weights.copy()
51+
self.bias = bias.copy()
52+
53+
class ReLU(Layer):
54+
name = 'relu'
55+
def __init__(self):pass
56+
57+
def forward(self, x):
58+
return (x > 0) * x
59+
60+
class Flatten(Layer):
61+
name = 'flatten'
62+
def __init__(self):pass
63+
64+
def forward(self, x):
65+
return x.reshape((1, -1))
66+
67+
class Sigmoid(Layer):
68+
name = 'sigmoid'
69+
def __init__(self):pass
70+
71+
def forward(self, x):
72+
return 1/(1 + np.exp(-x))
73+
74+
class Softmax(Layer):
75+
name = 'softmax'
76+
def __init__(self, axis=-1):
77+
self.axis = axis
78+
79+
def forward(self, x):
80+
eX = np.exp((x.T - np.max(x, axis=self.axis)).T)
81+
return (eX.T / eX.sum(axis=self.axis)).T
82+
83+
class Maxpool(Layer):
84+
name = 'maxpool'
85+
def __init__(self, stride=2):
86+
self.stride = stride
87+
88+
def para(self): return (self.stride,)
89+
90+
def forward(self, x):
91+
return maxpool(x, (self.stride, self.stride))
92+
93+
class UpSample(Layer):
94+
name = 'upsample'
95+
def __init__(self, k):
96+
self.k = k
97+
98+
def para(self): return (self.k,)
99+
100+
def forward(self, x):
101+
return upsample(x, self.k)
102+
103+
class Concatenate(Layer):
104+
name = 'concat'
105+
def __init__(self): pass
106+
107+
def forward(self, x):
108+
return np.concatenate(x, axis=1)
109+
110+
layerkey = {'dense':Dense, 'conv':Conv2d, 'relu':ReLU,
111+
'flatten':Flatten, 'sigmoid':Sigmoid, 'softmax': Softmax,
112+
'maxpool':Maxpool, 'upsample':UpSample, 'concat':Concatenate}
113+
114+
if __name__ == "__main__":
115+
pass

cnnumpy/net.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from .layer import layerkey as key
2+
import numpy as np
3+
import json
4+
5+
class Net:
6+
def __init__(self):
7+
self.body = []
8+
self.cmds = []
9+
10+
def load_json(self, body, cmds):
11+
for i in body:
12+
para = i[2] or []
13+
self.body.append((i[0], key[i[1]](*para)))
14+
self.cmds = cmds
15+
16+
def forward(self, x):
17+
dic = dict(self.body)
18+
rst = {self.cmds[0][0]: x}
19+
for x, ls, y in self.cmds:
20+
for l in ls:
21+
out = x if l == ls[0] else y
22+
if isinstance(out, list):
23+
p = [rst[i] for i in out]
24+
else: p = rst[out]
25+
rst[y] = dic[l](p)
26+
return rst[y]
27+
28+
def layer2code(self, style='list'):
29+
if style == 'list':
30+
body = ['self.body = [']
31+
for i in self.body:
32+
body.append('\t("%s", %s, %s),'%(i[0],
33+
i[1].__class__.__name__, i[1].para()))
34+
body.append(']')
35+
if style == 'self':
36+
body = []
37+
for i in self.body:
38+
body.append('self.%s = %s%s'%(i[0],
39+
i[1].__class__.__name__, i[1].para() or ()))
40+
return '\n'.join(body)
41+
42+
def layer2json(self):
43+
body = []
44+
invk = dict(zip(key.values(),key.keys()))
45+
for i in self.body:
46+
body.append((i[0], invk[i[1].__class__], i[1].para()))
47+
return body
48+
49+
def flw2code(self, style='list'):
50+
body = []
51+
if style=='list':
52+
body.append('dic = dict(self.body)')
53+
for x, ls, y in self.cmds:
54+
for l in ls:
55+
out = x if l == ls[0] else y
56+
if isinstance(out, list):
57+
out = str(out).replace("'",'')
58+
body.append("%s = dic['%s'](%s)"%(y,l,out))
59+
body.append('')
60+
if style=='self':
61+
for x, ls, y in self.cmds:
62+
for l in ls:
63+
out = x if l == ls[0] else y
64+
if isinstance(out, list):
65+
out = str(out).replace("'",'')
66+
body.append('%s = self.%s(%s)'%(y,l,out))
67+
body.append('')
68+
return '\n'.join(body)
69+
70+
def load_data(self, data, shpkey):
71+
s = 0
72+
for i in shpkey:
73+
for j in range(len(shpkey[i])):
74+
sp = shpkey[i][j]
75+
l = np.cumprod(sp)[-1]
76+
shpkey[i][j] = data[s:s+l].reshape(sp)
77+
s += l
78+
dic = dict(self.body)
79+
for i in shpkey:
80+
dic[i].K = shpkey[i][0]
81+
dic[i].bias = shpkey[i][1]
82+
83+
def __call__(self, x):
84+
return self.forward(x)
85+
86+
def read_net(path):
87+
net = Net()
88+
with open(path+'.lay') as f: lay = json.load(f)
89+
with open(path+'.flw') as f: flw = json.load(f)
90+
net.load_json(lay, flw)
91+
data = np.load(path+'.npy')
92+
with open(path+'.shp') as f: shp = json.load(f)
93+
net.load_data(data, shp)
94+
return net
95+
96+
if __name__ == '__main__':
97+
pass

cnnumpy/util.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import numpy as np
2+
from time import time
3+
try: from numba import njit
4+
except: njit = None
5+
6+
if njit is None: print('install numba can double speed!')
7+
8+
def neighbors(shape, core, offset=0):
9+
shp = [slice(0,i) for i in core]
10+
idx = np.mgrid[tuple(shp)]
11+
idx = idx.reshape((len(core),-1))
12+
offset = np.array(core)//2*offset
13+
idx -= offset.reshape((-1,1))
14+
acc = np.cumprod((1,)+shape[::-1][:-1])
15+
return np.dot(idx.T, acc[::-1])
16+
17+
def jit_fill_col(pdimg, idx, colimg):
18+
s = 0
19+
for i in range(len(pdimg)):
20+
if pdimg[i]&1==0: continue
21+
for j in idx:
22+
colimg[s] = pdimg[i+j]
23+
s += 1
24+
return colimg
25+
26+
def fill_col(pdimg, idx, colimg):
27+
rc = np.where(pdimg&1)[0]
28+
rc = rc.reshape((-1,1))+idx
29+
colimg[:] = pdimg[rc.ravel()]
30+
return colimg
31+
32+
if not njit is None: fill_col = njit(jit_fill_col)
33+
34+
def conv(img, core, stride=(1,1), buf=['']):
35+
# new the col_img, if needed
36+
strh, strw = stride
37+
cimg_w = np.cumprod(core.shape[1:])[-1]
38+
n,c,h,w = img.shape
39+
cimg_h = n*(h//strh)*(w//strw)
40+
if len(buf[0])<cimg_h*cimg_w:
41+
col_img = np.zeros(cimg_h*cimg_w, dtype=np.int32)
42+
buf[0] = col_img
43+
else:
44+
col_img = buf[0][:cimg_h*cimg_w]
45+
col_img[:] = 0
46+
# mark where need
47+
iimg = img.view(dtype=np.int32)
48+
iimg &= 0xfffffffe
49+
iimg[:,0,::strh,::strw] |= 1
50+
# ravel the image
51+
n,c,h,w = np.array(core.shape)
52+
shp = ((0,0),(0,0),(h//2,h//2),(w//2,w//2))
53+
pdimg = np.pad(iimg, shp, 'constant', constant_values=0)
54+
nbs = neighbors(pdimg.shape[1:], core.shape[1:], (0,1,1))
55+
fill_col(pdimg.ravel(), nbs, col_img)
56+
col_img = col_img.view(np.float32)
57+
col_img = col_img.reshape((cimg_h, cimg_w))
58+
# dot
59+
col_core = core.reshape((core.shape[0],-1))
60+
rst = col_core.dot(col_img.T)
61+
ni, ci, hi, wi = img.shape
62+
return rst.reshape((ni, n, hi//strh, wi//strw))
63+
64+
65+
def jit_fill_max(pdimg, idx, colimg):
66+
s = 0
67+
for i in range(len(pdimg)):
68+
if pdimg[i]&1==0: continue
69+
for j in idx:
70+
colimg[s] = max(colimg[s], pdimg[i+j])
71+
s += 1
72+
return colimg
73+
74+
def fill_max(pdimg, idx, colimg):
75+
rc = np.where(pdimg&1)[0]
76+
rc = rc.reshape((-1,1))+idx
77+
vs = pdimg[rc.ravel()].reshape((-1, len(idx)))
78+
np.max(vs, axis=-1, out=colimg)
79+
80+
if not njit is None: fill_max = njit(jit_fill_max)
81+
82+
def maxpool(img, stride=(2,2)):
83+
strh, strw = stride
84+
n,c,h,w = img.shape
85+
cimg_h = n*c*(h//strh)*(w//strw)
86+
87+
iimg = img.view(dtype=np.int32)
88+
iimg &= 0xfffffffe
89+
iimg[:,:,::strh,::strw] |= 1
90+
91+
nbs = neighbors(img.shape[1:], (1,)+stride)
92+
shp = (n, c, h//strh, w//strw)
93+
colimg = np.zeros(shp, dtype=np.int32)
94+
fill_max(iimg.ravel(), nbs, colimg.ravel())
95+
return colimg.view(np.float32)
96+
97+
def jit_resize(img, k, ra, rb, rs, _rs, ca, cb, cs, _cs, out):
98+
h, w = img.shape
99+
for r in range(h*k):
100+
rar = ra[r]
101+
rbr = rar+1
102+
rsr = rs[r]
103+
_rsr = _rs[r]
104+
for c in range(w*k):
105+
cac = ca[c]
106+
cbc = cac+1
107+
rra = img[rar,cac]*_rsr
108+
rra += img[rbr,cac]*rsr
109+
rrb = img[rar,cbc]*_rsr
110+
rrb += img[rbr,cbc]*rsr
111+
rcab = rra * _cs[c] + rrb * cs[c]
112+
out[r,c] = rcab
113+
114+
def resize(img, k, ra, rb, rs, _rs, ca, cb, cs, _cs, out):
115+
out[:img.shape[0]] = img[:,ca]*_cs + img[:,cb]*cs
116+
out[:] = (out[ra].T*_rs + out[rb].T*rs).T
117+
118+
if not njit is None: resize = njit(jit_resize)
119+
120+
def upsample(img, k, out=None):
121+
nc, (h, w) = img.shape[:-2], img.shape[-2:]
122+
if out is None:
123+
out = np.zeros(nc+(h*k, w*k), dtype=img.dtype)
124+
rs = np.linspace(-0.5+0.5/k, h-0.5-0.5/k, h*k, dtype=np.float32)
125+
cs = np.linspace(-0.5+0.5/k, w-0.5-0.5/k, w*k, dtype=np.float32)
126+
np.clip(rs, 0, h-1, out=rs)
127+
np.clip(cs, 0, w-1, out=cs)
128+
ra = np.floor(rs).astype(np.uint32)
129+
ca = np.floor(cs).astype(np.uint32)
130+
np.clip(ra, 0, h-1.5, out=ra)
131+
np.clip(ca, 0, w-1.5, out=ca)
132+
rs -= ra; cs -= ca;
133+
outcol = out.reshape((-1, h*k, w*k))
134+
imgcol = img.reshape((-1, h, w))
135+
for i, o in zip(imgcol, outcol):
136+
resize(i, k, ra, ra+1, rs, 1-rs, ca, ca+1, cs, 1-cs, o)
137+
return out
138+
139+
if __name__ == '__main__':
140+
from skimage.data import camera
141+
import matplotlib.pyplot as plt
142+
from scipy.ndimage import convolve
143+
img = np.zeros((1, 3, 512, 512), dtype=np.float32)
144+
#img.ravel()[:] = np.arange(3*512*512)
145+
core = np.zeros((32, 3, 3, 3), dtype=np.float32)
146+
#core.ravel()[:] = np.arange(3*3*3*32)
147+
148+
rst1 = conv(img, core, (1,1))
149+
start = time()
150+
rst1 = conv(img, core, (1,1))
151+
print('jit cost:', time()-start)
152+
153+
start = time()
154+
rst2 = conv(img, core, (1,1))
155+
print('numpy cost:', time()-start)

0 commit comments

Comments
 (0)