# -*- coding: utf-8 -*-
import copy
import pickle
import random
class NN(object):
def __init__(self, in_count, out_count):
assert isinstance(in_count, int) and in_count > 0, "Param 'in_count' must be int and more 0"
assert isinstance(out_count, int) and out_count > 0, "Param 'out_count' must be int and more 0"
self._in_count = in_count
self._out_count = out_count
self._neurons = []
self._neurons = [[round(random.random() / 10, 3) for _ in xrange(self._in_count)] for _ in xrange(out_count)]
self._fn = None
@staticmethod
def load_from_file(fn):
nn = NN(1, 1)
nn._load_from_file(fn)
nn._fn = fn
return nn
def _load_from_file(self, fn):
f = open(fn, 'rb')
obj = copy.deepcopy(pickle.load(f))
f.close()
self._in_count = obj['in_count']
self._out_count = obj['out_count']
self._neurons = obj['neurons']
def save_to_file(self, fn=None):
if fn is None:
if self._fn:
fn = self._fn
else:
raise AttributeError("Filename is unset")
f = open(fn, 'wb')
obj = {'in_count': self._in_count,
'out_count': self._out_count,
'neurons': self._neurons}
pickle.dump(copy.deepcopy(obj), f)
f.close()
self._fn = fn
def calc(self, in_data, out_count=None, return_list=False):
if out_count is None:
out_count = self._out_count
else:
assert isinstance(out_count, int) and 0 < out_count <= self._out_count,\
"Param 'out_count' must be int and between 1 and %d" % self._out_count
assert isinstance(in_data, list) and len(in_data) == self._in_count,\
"Param 'in_data' must be list and len must be %d" % self._in_count
if return_list:
assert out_count == self._out_count, "For 'return_list' param 'out_count' must be %s" % self._out_count
w = self.get_all_weight(in_data)
if return_list:
return w
else:
if self._out_count == out_count:
return dict(zip(xrange(self._out_count), w))
else:
out = {}
s = sorted(xrange(self._out_count), key=lambda k: w[k])
s.reverse()
for i in xrange(out_count):
out[s[i]] = w[s[i]]
return out
def get_weight(self, in_data, neuron_index):
assert isinstance(in_data, list) and len(in_data) == self._in_count,\
"Param 'in_data' must be list and len must be %d" % self._in_count
assert isinstance(neuron_index, int) and 0 <= neuron_index < self._out_count,\
"Param 'neuron_index' must be int and between 1 and %d" % (self._out_count - 1)
sum = 0
for i in xrange(self._in_count):
if in_data[i]:
sum += in_data[i] * self._neurons[neuron_index][i]
return sum
def get_all_weight(self, in_data):
assert isinstance(in_data, list) and len(in_data) == self._in_count,\
"Param 'in_data' must be list and len must be %d" % self._in_count
weights = [0] * self._out_count
for i in xrange(self._in_count):
if in_data[i]:
for n in xrange(self._out_count):
weights[n] += in_data[i] * self._neurons[n][i]
return weights
def training(self, in_data, out_index, add_weight=0.001, add_true_weight=0.00005):
assert isinstance(in_data, list) and len(in_data) == self._in_count,\
"Param 'in_data' must be list and len must be %d" % self._in_count
assert isinstance(out_index, int) and 0 <= out_index < self._out_count,\
"Param 'out_index' must be int and between 1 and %d" % (self._out_count - 1)
ix = self.calc(in_data, 1).popitem()[0]
if ix != out_index:
result, add = False, add_weight
else:
result, add = True, add_true_weight
if add:
for i in xrange(self._in_count):
if in_data[i]:
self._neurons[out_index][i] += in_data[i] * add
return result
def training_all(self, data, iterations=100, percent_errors=10, print_progress=False):
data_len = len(data)
if print_progress:
print "Training (iterations: %d, percent_errors: %.2f%%, count data: %d)..." %\
(iterations, percent_errors, data_len)
n = 0
while n < iterations:
fails = 0
for i in xrange(data_len):
if not self.training(data[i][0], data[i][1]):
fails += 1
errors = (float(fails) / data_len) * 100
if print_progress:
print 'Errors: %.2f%%' % errors
if errors <= percent_errors:
n += 1
if print_progress:
print 'Training complete: %.2f%%' % ((float(n) / iterations) * 100)
if print_progress:
print "Training completed sucessfully."
def test(self, data):
n = 0
for i in xrange(len(data)):
ix = self.calc(data[i][0], 1).popitem()[0]
if data[i][1] == ix:
n += 1
return (float(n) / len(data)) * 100
if __name__ == '__main__':
DATA_VALUES = 'XYZ'
data = [([1, 0, 0, 0, 1,
0, 1, 0, 1, 0,
0, 0, 1, 0, 0,
0, 1, 0, 1, 0,
1, 0, 0, 0, 1], DATA_VALUES.index('X')),
([1, 0, 0, 0, 1,
0, 1, 0, 1, 0,
0, 0, 1, 0, 0,
0, 0, 1, 0, 0,
0, 0, 1, 0, 0], DATA_VALUES.index('Y')),
([1, 1, 1, 1, 1,
0, 0, 0, 1, 0,
0, 0, 1, 0, 0,
0, 1, 0, 0, 0,
1, 1, 1, 1, 1], DATA_VALUES.index('Z'))]
test = [([1, 0, 0, 0, 1,
0, 1, 0, 1, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
1, 1, 0, 0, 1], DATA_VALUES.index('X')),
([1, 0, 0, 0, 1,
0, 1, 0, 1, 0,
0, 0, 1, 0, 0,
1, 0, 1, 0, 0,
0, 0, 0, 0, 0], DATA_VALUES.index('Y')),
([1, 1, 1, 1, 1,
0, 0, 0, 1, 0,
0, 0, 1, 0, 1,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1], DATA_VALUES.index('Z'))]
nn = NN(5 * 5, len(DATA_VALUES))
nn.training_all(data, 10000, 0.1, True)
print 'Test result %.2f%%' % nn.test(test)