Skip to content

Commit f970d7c

Browse files
author
Mark Poscablo
committed
Added logistic regression example
1 parent 9cea077 commit f970d7c

File tree

5 files changed

+374
-0
lines changed

5 files changed

+374
-0
lines changed

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "assets"]
2+
path = assets
3+
url = https://github.com/arrayfire/assets.git

Diff for: assets

Submodule assets added at 6b13342

Diff for: examples/common/idxio.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/python
2+
3+
#######################################################
4+
# Copyright (c) 2019, ArrayFire
5+
# All rights reserved.
6+
#
7+
# This file is distributed under 3-clause BSD license.
8+
# The complete license agreement can be obtained at:
9+
# http://arrayfire.com/licenses/BSD-3-Clause
10+
########################################################
11+
12+
def reverse_char(b):
13+
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4
14+
b = (b & 0xCC) >> 2 | (b & 0x33) << 2
15+
b = (b & 0xAA) >> 1 | (b & 0x55) << 1
16+
return b
17+
18+
19+
# http://stackoverflow.com/a/9144870/2192361
20+
def reverse(x):
21+
x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1)
22+
x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2)
23+
x = ((x >> 4) & 0x0f0f0f0f) | ((x & 0x0f0f0f0f) << 4)
24+
x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8)
25+
x = ((x >> 16) & 0xffff) | ((x & 0xffff) << 16);
26+
return x
27+
28+
29+
def read_idx(name):
30+
with open(name, 'rb') as f:
31+
# In the C++ version, bytes the size of 4 chars are being read
32+
# May not work properly in machines where a char is not 1 byte
33+
bytes_read = f.read(4)
34+
bytes_read = bytearray(bytes_read)
35+
36+
if bytes_read[2] != 8:
37+
raise RuntimeError('Unsupported data type')
38+
39+
numdims = bytes_read[3]
40+
elemsize = 1
41+
42+
# Read the dimensions
43+
elem = 1
44+
dims = [0] * numdims
45+
for i in range(numdims):
46+
bytes_read = bytearray(f.read(4))
47+
48+
# Big endian to little endian
49+
for j in range(4):
50+
bytes_read[j] = reverse_char(bytes_read[j])
51+
bytes_read_int = int.from_bytes(bytes_read, 'little')
52+
dim = reverse(bytes_read_int)
53+
54+
elem = elem * dim;
55+
dims[i] = dim;
56+
57+
# Read the data
58+
cdata = f.read(elem * elemsize)
59+
cdata = list(cdata)
60+
data = [float(cdata_elem) for cdata_elem in cdata]
61+
62+
return (dims, data)
63+
64+

Diff for: examples/machine_learning/logistic_regression.py

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!/usr/bin/python
2+
3+
#######################################################
4+
# Copyright (c) 2019, ArrayFire
5+
# All rights reserved.
6+
#
7+
# This file is distributed under 3-clause BSD license.
8+
# The complete license agreement can be obtained at:
9+
# http://arrayfire.com/licenses/BSD-3-Clause
10+
########################################################
11+
12+
from mnist_common import display_results, setup_mnist
13+
14+
import sys
15+
import time
16+
17+
import arrayfire as af
18+
from arrayfire.algorithm import max, imax, count, sum
19+
from arrayfire.arith import abs, sigmoid, log
20+
from arrayfire.array import transpose
21+
from arrayfire.blas import matmul, matmulTN
22+
from arrayfire.data import constant, join, lookup, moddims
23+
from arrayfire.device import set_device, sync, eval
24+
25+
26+
def accuracy(predicted, target):
27+
_, tlabels = af.imax(target, 1)
28+
_, plabels = af.imax(predicted, 1)
29+
return 100 * af.count(plabels == tlabels) / tlabels.elements()
30+
31+
32+
def abserr(predicted, target):
33+
return 100 * af.sum(af.abs(predicted - target)) / predicted.elements()
34+
35+
36+
# Predict (probability) based on given parameters
37+
def predict_proba(X, Weights):
38+
Z = af.matmul(X, Weights)
39+
return af.sigmoid(Z)
40+
41+
42+
# Predict (log probability) based on given parameters
43+
def predict_log_proba(X, Weights):
44+
return af.log(predict_proba(X, Weights))
45+
46+
47+
# Give most likely class based on given parameters
48+
def predict(X, Weights):
49+
probs = predict_proba(X, Weights)
50+
_, classes = af.imax(probs, 1)
51+
return classes
52+
53+
54+
def cost(Weights, X, Y, lambda_param=1.0):
55+
# Number of samples
56+
m = Y.dims()[0]
57+
58+
dim0 = Weights.dims()[0]
59+
dim1 = Weights.dims()[1] if len(Weights.dims()) > 1 else None
60+
dim2 = Weights.dims()[2] if len(Weights.dims()) > 2 else None
61+
dim3 = Weights.dims()[3] if len(Weights.dims()) > 3 else None
62+
# Make the lambda corresponding to Weights(0) == 0
63+
lambdat = af.constant(lambda_param, dim0, dim1, dim2, dim3)
64+
65+
# No regularization for bias weights
66+
lambdat[0, :] = 0
67+
68+
# Get the prediction
69+
H = predict_proba(X, Weights)
70+
71+
# Cost of misprediction
72+
Jerr = -1 * af.sum(Y * af.log(H) + (1 - Y) * af.log(1 - H), dim=0)
73+
74+
# Regularization cost
75+
Jreg = 0.5 * af.sum(lambdat * Weights * Weights, dim=0)
76+
77+
# Total cost
78+
J = (Jerr + Jreg) / m
79+
80+
# Find the gradient of cost
81+
D = (H - Y)
82+
dJ = (af.matmulTN(X, D) + lambdat * Weights) / m
83+
84+
return J, dJ
85+
86+
87+
def train(X, Y, alpha=0.1, lambda_param=1.0, maxerr=0.01, maxiter=1000, verbose=False):
88+
# Initialize parameters to 0
89+
Weights = af.constant(0, X.dims()[1], Y.dims()[1])
90+
91+
for i in range(maxiter):
92+
# Get the cost and gradient
93+
J, dJ = cost(Weights, X, Y, lambda_param)
94+
95+
err = af.max(af.abs(J))
96+
if err < maxerr:
97+
print('Iteration {0:4d} Err: {1:4f}'.format(i + 1, err))
98+
print('Training converged')
99+
return Weights
100+
101+
if verbose and ((i+1) % 10 == 0):
102+
print('Iteration {0:4d} Err: {1:4f}'.format(i + 1, err))
103+
104+
# Update the parameters via gradient descent
105+
Weights = Weights - alpha * dJ
106+
107+
if verbose:
108+
print('Training stopped after {0:d} iterations'.format(maxiter))
109+
110+
return Weights
111+
112+
113+
def benchmark_logistic_regression(train_feats, train_targets, test_feats):
114+
t0 = time.time()
115+
Weights = train(train_feats, train_targets, 0.1, 1.0, 0.01, 1000)
116+
af.eval(Weights)
117+
sync()
118+
t1 = time.time()
119+
dt = t1 - t0
120+
print('Training time: {0:4.4f} s'.format(dt))
121+
122+
t0 = time.time()
123+
iters = 100
124+
for i in range(iters):
125+
test_outputs = predict(test_feats, Weights)
126+
af.eval(test_outputs)
127+
sync()
128+
t1 = time.time()
129+
dt = t1 - t0
130+
print('Prediction time: {0:4.4f} s'.format(dt / iters))
131+
132+
133+
# Demo of one vs all logistic regression
134+
def logit_demo(console, perc):
135+
# Load mnist data
136+
frac = float(perc) / 100.0
137+
mnist_data = setup_mnist(frac, True)
138+
num_classes = mnist_data[0]
139+
num_train = mnist_data[1]
140+
num_test = mnist_data[2]
141+
train_images = mnist_data[3]
142+
test_images = mnist_data[4]
143+
train_targets = mnist_data[5]
144+
test_targets = mnist_data[6]
145+
146+
# Reshape images into feature vectors
147+
feature_length = int(train_images.elements() / num_train);
148+
train_feats = af.transpose(af.moddims(train_images, feature_length, num_train))
149+
test_feats = af.transpose(af.moddims(test_images, feature_length, num_test))
150+
151+
train_targets = af.transpose(train_targets)
152+
test_targets = af.transpose(test_targets)
153+
154+
num_train = train_feats.dims()[0]
155+
num_test = test_feats.dims()[0]
156+
157+
# Add a bias that is always 1
158+
train_bias = af.constant(1, num_train, 1)
159+
test_bias = af.constant(1, num_test, 1)
160+
train_feats = af.join(1, train_bias, train_feats)
161+
test_feats = af.join(1, test_bias, test_feats)
162+
163+
# Train logistic regression parameters
164+
Weights = train(train_feats, train_targets,
165+
0.1, # learning rate
166+
1.0, # regularization constant
167+
0.01, # max error
168+
1000, # max iters
169+
True # verbose mode
170+
)
171+
af.eval(Weights)
172+
af.sync()
173+
174+
# Predict the results
175+
train_outputs = predict_proba(train_feats, Weights)
176+
test_outputs = predict_proba(test_feats, Weights)
177+
178+
print('Accuracy on training data: {0:2.2f}'.format(accuracy(train_outputs, train_targets)))
179+
print('Accuracy on testing data: {0:2.2f}'.format(accuracy(test_outputs, test_targets)))
180+
print('Maximum error on testing data: {0:2.2f}'.format(abserr(test_outputs, test_targets)))
181+
182+
benchmark_logistic_regression(train_feats, train_targets, test_feats)
183+
184+
if not console:
185+
test_outputs = af.transpose(test_outputs)
186+
# Get 20 random test images
187+
display_results(test_images, test_outputs, af.transpose(test_targets), 20, True)
188+
189+
def main():
190+
argc = len(sys.argv)
191+
192+
device = int(sys.argv[1]) if argc > 1 else 0
193+
console = sys.argv[2][0] == '-' if argc > 2 else False
194+
perc = int(sys.argv[3]) if argc > 3 else 60
195+
196+
try:
197+
af.set_device(device)
198+
af.info()
199+
logit_demo(console, perc)
200+
except Exception as e:
201+
print('Error: ', str(e))
202+
203+
204+
if __name__ == '__main__':
205+
main()

Diff for: examples/machine_learning/mnist_common.py

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/usr/bin/python
2+
3+
#######################################################
4+
# Copyright (c) 2019, ArrayFire
5+
# All rights reserved.
6+
#
7+
# This file is distributed under 3-clause BSD license.
8+
# The complete license agreement can be obtained at:
9+
# http://arrayfire.com/licenses/BSD-3-Clause
10+
########################################################
11+
12+
import sys
13+
sys.path.insert(0, '../common')
14+
from idxio import read_idx
15+
16+
import arrayfire as af
17+
from arrayfire.algorithm import where
18+
from arrayfire.array import Array
19+
from arrayfire.data import constant, lookup, moddims
20+
from arrayfire.random import randu
21+
22+
23+
def classify(arr, k, expand_labels):
24+
ret_str = ''
25+
if expand_labels:
26+
vec = arr[:, k].as_type(af.Dtype.f32)
27+
h_vec = vec.to_list()
28+
data = []
29+
30+
for i in range(vec.elements()):
31+
data.append((h_vec[i], i))
32+
33+
data = sorted(data, key=lambda pair: pair[0], reverse=True)
34+
35+
ret_str = str(data[0][1])
36+
37+
else:
38+
ret_str = str(int(arr[k].as_type(af.Dtype.f32).scalar()))
39+
40+
return ret_str
41+
42+
43+
def setup_mnist(frac, expand_labels):
44+
idims, idata = read_idx('../../assets/examples/data/mnist/images-subset')
45+
ldims, ldata = read_idx('../../assets/examples/data/mnist/labels-subset')
46+
47+
idims.reverse()
48+
numdims = len(idims)
49+
images = af.Array(idata, tuple(idims))
50+
51+
R = af.randu(10000, 1);
52+
cond = R < min(frac, 0.8)
53+
train_indices = af.where(cond)
54+
test_indices = af.where(~cond)
55+
56+
train_images = af.lookup(images, train_indices, 2) / 255
57+
test_images = af.lookup(images, test_indices, 2) / 255
58+
59+
num_classes = 10
60+
num_train = train_images.dims()[2]
61+
num_test = test_images.dims()[2]
62+
63+
if expand_labels:
64+
train_labels = af.constant(0, num_classes, num_train)
65+
test_labels = af.constant(0, num_classes, num_test)
66+
67+
h_train_idx = train_indices.to_list()
68+
h_test_idx = test_indices.to_list()
69+
70+
for i in range(num_train):
71+
train_labels[ldata[h_train_idx[i]], i] = 1
72+
73+
for i in range(num_test):
74+
test_labels[ldata[h_test_idx[i]], i] = 1
75+
76+
else:
77+
labels = af.Array(ldata, tuple(ldims))
78+
train_labels = labels[train_indices]
79+
test_labels = labels[test_indices]
80+
81+
return (num_classes,
82+
num_train,
83+
num_test,
84+
train_images,
85+
test_images,
86+
train_labels,
87+
test_labels)
88+
89+
90+
def display_results(test_images, test_output, test_actual, num_display, expand_labels):
91+
for i in range(num_display):
92+
print('Predicted: ', classify(test_output, i, expand_labels))
93+
print('Actual: ', classify(test_actual, i, expand_labels))
94+
95+
img = (test_images[:, :, i] > 0.1).as_type(af.Dtype.u8)
96+
img = af.moddims(img, img.elements()).to_list()
97+
for j in range(28):
98+
for k in range(28):
99+
print('\u2588' if img[j * 28 + k] > 0 else ' ', end='')
100+
print()
101+
input()

0 commit comments

Comments
 (0)